您现在的位置是:首页 >学无止境 >Java-Kotlin-一次性掌握两门语言网站首页学无止境

Java-Kotlin-一次性掌握两门语言

阿卢说他遇到阿玮 2024-06-17 10:29:57
简介Java-Kotlin-一次性掌握两门语言

简介

本文主要是通过介绍Java和Kotlin的基础语法达成极速入门。
Kotlin本质是适合带有一定Java基础或者偏现代语法例如Typescript会更容易上手。

Java特点

  • 提供了字节码与虚拟机组合的方式让跨操作系统运行非常轻松简单,在运行时才转成系统所使用的机器码
  • 源于C++,极致的面向对象,一个文件就是一个类,类继承于对象,万物皆对象,没有C++的头文件概念,而使用interface接口抽象化各种参数与约束实现类
  • 没有命名空间,只有包+类+方法,不可使用函数式编程,或者说只能在类里定义静态方法
  • 通过类与接口的继承、组合、实现既严格又灵活的语法特点,缺点是一个函数调用可以完成的工作变成三行代码加三四个分布在不同文件中的类与接口
  • 通过IOC、AOP的特性搭配注解提供了简化依赖、无侵入式编程特性
  • 不支持运算符重载,导致封装类型需要调用内部方法传参进行运算

Kotlin特点

  • 与Java代码可互转,并且同样是编译为字节码,各种库包可以无缝互相调用
  • 遵循现代化开发语言特点,将类型后置,甚至可不用定义类型,由值自动推断类型
  • 大量使用Java中后期才提供的Lambda表达式,并把类方法重新变回简化的函数
  • 防止null值出现
  • 行末无须分号结束
  • 多使用花括号{}作为载体体现语法特性
  • 无自动隐形转换,例如整型1加字符串"1"在Java等语言中会自动转成字符串,但在Kotlin里会报错,要求必须手动将整型1转换成字符串才行
  • 支持运算符重载,因此封装类型可以直接运算

类型

原始类型

Java可用而Kotlin隐藏了的原始类型,这些类型源于C++,性能最好,但缺少细节处理和无法当作类来调用其中的方法。

包括(注意首字母小写):

  • byte
  • int
  • short
  • float
  • double

封装类型

Java和Kotlin都具有的封装类型,实际是个类类型。
这些类型是将原始类型值放到类属性里,使用时直接使用类,性能相比原始类型只有可忽略的损耗,实例化时有对内存和数据溢出或null的细节处理,也可作为类调用内部各种方法。
实际Java也更鼓励使用封装类型而不是原始类型。

包括(注意首字母大写):

  • Byte
  • Short
  • Int
  • Long
  • Float
  • Double
  • String
class Zava {
    //  整型常量,不可修改
    final int const1 = 2;
    //  整型变量,可修改
    int variable1 = 1;
    //  封装整型变量,原始类型强化
    Int variable2 = Int.valueOf(1);
}
//  常量
val con1 = 1
//  相当于
val con1: Int = 1
//  常量不可修改值
//con1 = 2
//  变量
var con2 = 1
//  同样相当于
var con2: Int = 1
//  变量可修改值
con2 = 2
//con2 = "1"  //  No allow, type are Int
//  字符串,可拼接变量
var cont3 = "this is string with value: ${con1}"
//  相当于
var cont3: String = "this is string with value: " + con1
//  多行字符串和Java一样使用```包裹即可
/*var cont4 = ```
    multiply
    line
    string
```*/
//  不可变数组
var arr = arrayOf(1, 2, 3)
//  可变列表
var arr = mutableListof(1, 2, 3)

判断

Java与其他语言一样使用if、else、switch:

class Kava {
    init() {
        int i = 2;
        int j;
        String k;
        if (i) {
            j = 20;
        } else {
            k = "20";
        }
        switch (i) {
            case 20:
                j = 20;
                break;
            default:
                k = "20";
                break;
        }
    }
}

Kotlin鼓励用when、is、else代替if、else和switch,更具有可读性,并可以直接根据判断赋值或返回值给外部使用:
if判断:

var i = 2
var j = when (i) {
    is Int -> 20
    else -> "20"
}

switch判断:

var i = 2
var j = when (i) {
    1 -> 10
    2 -> 20
    3 -> 30
    else -> "as default"
}

循环

Java有普通的for循环、增强for、数组类型的迭代器、while,而Kotlin没有普通的for循环,只有范围for、增强for、数组类型的迭代器,普通for循环只能用while代替。

普通for与while

java有普通or和while循环:

class Kava {
    init() {
        //  普通for
        for (int i = 0; i < 10; i++) {
            System.out.println(i);
        }
        //  while
        int i = 0;
        int length = 10;
        while (i < length) {
            System.out.println(i++);
        }
    }
}

kotlin有while循环:

var i = 0
var length = 10
while (i < length) {
    println(i++)
}

范围for

kotlin独有的范围for:

//  范围循环:从某个数到另外一个数,默认步进值为1,每次循环自增1
for (i in 0 until 10) {
    println(i)  //  输出 0,1,2,3,4,5,6,7,8,9,10
}
//  范围循环:从某个数到另外一个数,并指定步进值,每次循环按步进值自增
for (i in 0 until 10 step 2) {
    println(i)  //  输出 0,2,4,6,8,10
}
//  范围循环,但是自减
for (i in 10 downTo 0) {
    println(i)  //  输出 10,9,8,7,6,5,4,3,2,1,0
}
//  范围循环,使用自动判断增减的省略号..
for (i in 0..10) {
    println(i)  //  输出 0,1,2,3,4,5,6,7,8,9,10
}

增强for

Java使用冒号:来输出增强for:

class Kava {
    init() {
        int[] arr = new int[]{1, 2, 3};
        for (int value : arr) {
            System.out.println(value);
        }
    }
}

Kotlin用in关键字来输出增强for:

var arr = arrayOf(1, 2, 3)
for (value in arr) {
    println(value)
}

Kotlin还可输出索引

var arr = arrayOf(1, 2, 3)
for (index in arr.indices) {
    println(index)
}

Kotlin也可同时输出索引和值

var arr = arrayOf(1, 2, 3)
for (it in arr.valueWithIndex()) {
    println("${it.index}:${it.value}")
}

迭代器

Java输出值:

import java.util.ArrayList;
import java.util.function.Consumer;

class Kava {
    init() {
        ArrayList<String> arr = new ArrayList<>("hello", "world", "please");
        //  传统Java实现接口的方式
        arr.forEach(new Consumer() {
            void accept(String str) {
                System.out.println(str);
            }
        });
        //  后续推荐更简洁的Lambda表达式,类似匿名方法
        arr.forEach((str) -> {
            System.out.println(str);
        });
        //  如果已有相同参数与返回值的方法,也能用【包.类::方法名】引用,同属于Lambda语法
        arr.forEach(System.out::println);
    }
}

Kotlin输出值:

var arr = arrayOf("hello", "world", "please")
//  直接输出值,第一个参数会自动命名为it传入
arr.forEach {
    println(it)
}
//  上面方式与以下相同
arr.forEach({ index ->
    {
        println(index)
    }
})

Kotlin输出值和索引:

//  输出索引和值,使用匿名方法
arr.forEachIndexed { index, value ->
    println(index, value)
}
//  输出索引和值,定义方法并引入使用
fun iteratorInt(index: Int, value: Int) {
    println(index, value)
}
arr.forEachIndexed(::iteratorInt)

跳出外层循环

Java通过名称: for命名for循环,通过break 名称指定要跳出的循环体:

class M {
    init() {
        int sum = 0;
        out:
        for (int i = 1; i < 10; i++) {
            for (int j = 1; j < 4; j++) {
                sum *= (i + j);
                if (sum > 100) {
                    break out;
                }
            }
        }
    }
}

Kotlin通过名称@ for命名for循环,通过break@名称指定要跳出的循环体:

var sum = 0
out@ for (i in range(1, 10)) {
    for (j in range(1, 4)) {
        sum *= (i + j)
        when {
            sum > 100 -> break@out
        }
    }
}
println("跳出后到了这里")

函数

Java里没有所谓函数function,只有定义在类里的方法method,也可以说Java里的函数指代的就是方法。

class M {
    static int handle(int value) {
        return value * value * 2;
    }

    public static void main(String[] args) {
        System.out.println(handle(2));
    }
}

Kotlin相比Java而言重新体现函数的重要性,使用函数代替大部分Java里的类定义,其中就包括了main函数(Java里只能定义成类里的main方法)

fun handle(value: Int): Int {
    //  最后一行可以不用return即可将表达式结果作为返回值
    value * value * 2
}
fun main() {
    println(handle(2))
}

类与接口

Java的类与接口是最为重要的一环,通过接口抽象定义约束,然后交给类具体实现。
其中实现接口使用implements关键字,语法[class 类名 implements 接口名]。
继承类使用extends关键字,语法[class 类名 extends 父类名],父类即要被继承的类,继承了父类的类则称为子类。
定义构造方法只需要定义与类型相同的方法即可:

interface Live {
    String info();
}

abstract class Person {
    private final String name = "human";
}

class Man extends Person implements Live {
    private String name;
    private int age;

    //  定义一个无参的构造方法
    Man() {

    }

    //  定义一个多惨的构造方法
    Man(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //  定义一个获取私有属性的方法
    getName() {
        return name;
    }

    //  定义一个设置私有属性的方法
    setName(String _name) {
        name = _name;
    }

    // 实现接口要求的方法
    String info() {
        return "This is " + name;
    }
}

Kotlin的类无须new即可创建实例,注意这只是语法糖,内部依旧是使用new,与C++无new则是栈上分配不一样,而是保持与Java等自动指针管理的开发语言一样。
在类名后添加括号可填写默认构造函数的形参列表,需要重构方法则在内部使用constructor关键字即可

采用冒号形式表示继承(类)和实现(接口),其中继承类需要加上小括号表示执行构造函数,并且要注意,默认所有类都是不可被继承的,所有方法都是不可被重写的,类和方法会自动添加final关键字,需要在被继承的类和被重写的方法前用open关键字修饰,而重写的新方法则用override关键字修饰

interface Live {
    fun info(): String
}
open class Person {
    private val name: String = "human"
}
class Man : Person(), Live {
    fun info(): String = "This is $name"
}

Kotlin的构造方法使用constructor关键字,可添加不同参数重写为多个构造方法,如果只有一个默认构造方法,可以定义在类名后。

interface Live {
    fun info(): String
}
class Person(var name: String, var age: Int) : Live {
    private var name
        get() {
            return "The name is : $name"
        }
        set(value) {
            name = value
        }
    private var age

    //  定义构造方法
    constructor(name: String) : this(name, 1)
    constructor() : this("无名氏")

    fun info(): String {
        return this.toString()
    }
}

Kotlin可以通过添加data修饰符可以让类自动重写toString, composeTo, hashCode等方法。
也可以通过添加by可以使用委托的形式让其他类完成所需的事情,例如此处让必须实现的方法info委托给Person处理,Man自身可以不实现:

data class Man : Live by Person {
    fun work() {
        println("Go to work")
    }
}

Kotlin还有另一种通过by方式实现属性值的委托,包含了重载运算符
特性重载了赋值和获取值的行为


class FamilyChoose {
    operator fun getValue(thisReference: Any, property: KProperty<*>): String {
        return v
    }
    operator fun setValue(thisReference: Any, property: KProperty<*>, i: String) {
        v = i
    }
}
class Woman : Person {
    var workType by FamilyChoose()
}

枚举类

通过枚举类可以完成罗列所需的有限集合,例如罗列用户的职业身份,方柏霓使用带有语义的身份名称进行各种判断。
属性值实际是继承自一个object类的实例,所以可以进行各种类操作,例如用于比较、输出字符串等。
由于属性值都是实例,所以在任何地方引入使用都是相同值,但实例也造成了必须长时间占用堆内存无法回收。

在Java中,虽然叫枚举类,但使用的关键字是enum而没有class,注意可以通过括号指定实际值,否则就是按照0,1,2,3的顺序定义实际值:

public enum Type {
    //  定义语义化名称,实际只是定义语义化的类实例,内部还是整型值
    TEACHER, WORKER, ENGINEER(10), DESIGNER;
    //  可以像类一样定义属性和方法
    private final int _default = ENGINEER;
    Type getDefault() {
        return _default;
    }
}
class Kava {
    void init() {
        //  比较两个枚举值
        bool isSame = Type.TEACHER.equals(Type.WORKER);
    }
}

而Kotlin采用enum class关键字定义枚举类:

enum class Type {
    //  定义语义化名称,实际只是定义语义化的类实例,内部还是整型值
    TEACHER, WORKER, ENGINEER(10), DESIGNER;
    //  可以像类一样定义属性和方法
    private val _default = ENGINEER
    fun getDefault() = _default
}
//  比较两个枚举值
val isSame = Type.TEACHER.equals(Type.WORKER)

密封类

Kotlin特有的属于枚举类的扩展类,相比起来属性值直接使用了类本身而不再是实例,当在使用时才创建实例,所以可被回收,虽然每次创建的实例不同,但可以直接对比类本身来确认是否相同

sealed class PartTimeJob {
    class DRIVER : Boolean
    class BATMAN : Boolean
}

fun handle(partTimeJob: PartTimeJob) {
    when (partTimeJob) {
        is PartTimeJob.BATMAN -> println("I'm batman")
        else -> println("Just a normal guy trying to live")
    }
}
fun main(wantToByHero: Boolean) {
    val partTimeJob = if (wantToByHero) PartTimeJob.BATMAN() else PartTimeJob.DRIVER()
    handle(partTimeJob)
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。