kotlin类型兼容java类型的所有语义和概念,可是也并不是彻底相同,不过在kotlin中,一个类型于java中的同样,也包含以下元素:java
- 构造器和初始化模块
- 成员函数
- 属性
- 内部类
- 对象声明
构造函数
构造函数其实并非一个真正的函数,由于它没有返回值类型,连函数名也被严格约束。而从编译器的角度看,构造函数的确不是函数,由于编译器一般会对构造函数进行特别处理,在C++中,构造函数会被处理成内存分配指令,在java中,会被处理成new指令。所以构造函数能够被看着一个语法糖层面的伪函数。数组
- 构造函数声明
//直接在类型名称后面紧跟参数列表,完成的构造函数定义,叫主构造函数 //只须要一行就实现了属性定义和构造函数的声明,比java简洁太多 //显示的声明了有参主构造函数,就会覆盖默认的无参构造函数 class SharedBike (var name:String,var color:Int) { //kotlin 将init块,添加进了 构造函数中的 init { println("在构造函数中,进行一点别的逻辑功能") } var price:Int = 0 //二级构造函数 必须 间接或者直接代理 主构造函数 this(), 其实这也正是kotlin源程序中以 .kt 结尾的类型声明要添加括号的缘由 constructor(name: String,color: Int,price:Int):this(name,color){ this.price = price } } fun main() { val sharedBike = SharedBike("哈罗",0xff0000) println(sharedBike.name) println(sharedBike.color) }
- 构造函数的权限声明
class SharedBike private constructor(var name: String, var color: Int) { var price: Int = 0 //次级构造函数声明访问权限,直接在constructor关键字前添加就能够了 public constructor(name: String, color: Int, price: Int) : this(name, color) { this.price = price } } fun main() { //主构造函数被声明为私有,就只能调用次级构造函数了 ,主构造函数声明访问权限,必须添加关键字 constructor val sharedBike = SharedBike("哈罗", 0xff0000, 256) println(sharedBike.name) println(sharedBike.color) }
- 初始化顺序
class Animal() { //声明时初始化 var name:String = getName() constructor(name:String):this(){ println("构造函数时初始化") this.name = name } init { name = "name_from_init" println("init 块中初始化") } fun printName(){ println("name = $name") } } fun getName():String{ println("声明时初始化。。。") return "name_from_declare" } fun main() { val animal = Animal("name_from_constructor") animal.printName() }
结果:安全
声明时初始化。。。 init 块中初始化 构造函数时初始化 name = name_from_constructoride
事实上之因此是这样一个顺序,于JVM虚拟机实例化对象的整体策略有关,当JVM实例化Animal时,会依次执行以下逻辑: 1 在路径下找到 Animal.class 2 在JVM的heap内存域(即堆区)为Animal实例分配足够的内存空间。 3 将Animal实例内存空间清零,将其实例对象内的各个基本类型的字段都设置为对应的默认值。 4 若是字段在声明时进行了初始化,则按顺序执行各个字段的初始化逻辑 5 若是定义init{}块,则执行init{}块中的逻辑 6 若是定义了构造函数,则执行构造函数中的逻辑函数
属性
属性String 和String? 是不一样的,因此 属性String的值不能赋值给 String?this
class Animal() { //声明属性 直接赋值 var name = "张三" //声明一个能够为空的属性, 添加了 ? 表示这个属性能够为null,不然属性不能为空值 var height:Int? = null //强制 设置一个属性为空 , 会报错 // var weight :String = null!! //声明一个abstract的属性,能够不用赋值,可是对应的其所在的类 必须也是 abstract 修饰的 // abstract var hobby:String }
- 属性的空安全
class Animal { var name:String?= null fun test(){ name?.let { println(it) } println(name?.length) } } fun main() { val animal = Animal() animal.test() animal.name = "小白兔" println("-----------赋值后------------------") animal.test() }
结果:设计
null 可控属性不加let块 执行为空,加了let块 与if 判断同样 -----------赋值后------------------ 小白兔 3代理
访问权限
kotlin 中顶级变量和类属性默认状况下的访问权限都是public。这一点与java彻底不一样,java中默认状况下都是private。code
class Animal { var name:String = "二哈" private set //设置赋值方法为private } fun main() { val animal = Animal() //赋值报错 animal.name = "sldfk" }
数组
kotlin 的大部分语法特性都是基于java的,编写习惯也没有太大变化,而数组是个例外。对象
数组的初始化
- 经过Array接口声明数组
//声明一个 大小为3,初始值都是0的数组 ,"it->"能够省略 var asc = Array(3,{it -> 0}) // 若是不想kotlin自动推断类型,能够在声明数组的时候显示标记 var asc:Array<Int> = Array(3) { 0} // array 也能够根据步长来 初始化值 var asc:Array<Int> = Array(3) { it * 2}
- 数组读写
val asc:Array<Int> = Array(3) { it * 2} //写数组 asc[0] = 12 asc.set(1,24) //读数组 val a = asc[0] val b = asc.get(1)
- 声明一个引用类型数组
class Animal(val name:String,age:Int) { var age:Int = age override fun toString(): String { return "dog name = $name, age = $age,height = ${age * 2}" } } fun main() { val asc = Array(3) { Animal(it.toString(),it + 1)} for (a in asc){ println(a.toString()) } /** * dog name = 0, age = 1,height = 2 dog name = 1, age = 2,height = 4 dog name = 2, age = 3,height = 6 */ }
- 其余声明数组的方式
fun main() { //声明一个Animal 类型的大小为5的数组,可是没有对数组元素进行初始化,元素都为null val asc = arrayOfNulls<Animal>(5) //声明一个Int类型的数组,大小为3,默认初始值为0,其余基本类型也可使用相似的方法进行初始化 val arr = IntArray(3) for (i in asc) println(i) //声明java中的包装类型,(做参数传入须要包装类型的java方法中时,会自动装箱) val array = arrayOfNulls<Int>(2) array[0] = 12 array[1] = 2 }
- 多维数组
//声明一个二维数组,以下声明了一个2行三列的数组 val asc = Array(2){Array(3){0} } //遍历多维数组 for (i in asc.indices){ for (j in asc[i].indices){ println("i = $i,j = $j") } } //读写多维数组 asc[0][2] = 12 val asc02 = asc[0][2] println(asc02) //声明一个引用类型的多维数组 val an = Array(3){ arrayOfNulls<Animal>(3) }
- 数组与列表转换 在实际开发中列表的使用大于数组,由于列表的长度能够动态改变,数组不行。
val asc = IntArray(3) asc[0] = 12 asc[1] = 16 asc[2] = 19 val array2list = asc.asList() for (e in array2list){ println(e) } //声明一个列表 val list = ArrayList<Int>(2) list.add(1) list.add(2) list.add(3) for (e in list){ println(e) } //将列表转成数组 val ints = list.toArray() for (i in ints.indices) println(i)
静态函数和伴随对象
kotlin中的类不像java中的类,没有静态方法和静态变量,在kotlin类中编写的函数和属性,必须经过类实例对象才能访问,而不能直接经过类名访问。kotlin 特别设计了"伴随对象"。伴随对象顾名思义是一个对象声明,要定义一个伴随对象很简单,经过 companion object 关键字定义,实例以下:
class Animal() { var name:String? = null //声明一个伴随对象 InnerAnimal,并在其中定义函数run(), // 从外面能够直接经过Animal类型先到名 做为前缀调用该伴随对象的方法 companion object InnerAnimal{ fun run(){ println("fun run running .......") } } //一个类中 只能定义一个伴随对象 //伴随对象的名称能够省略,好比这里的 InnerAnimal 能够不写 // companion object { // fun run(){ // println("fun run running .......") // } // } } fun main() { Animal.run() }
- 伴随对象 由于是一个对象声明,因此不能实例化
- 伴随对象中的属性 在伴随对象中,不只能够声明方法,也能够定义变量。所定义的变量也能够经过伴随对象的宿主类直接访问,看起来就像java类中的静态变量同样
class Animal() { var name:String? = null companion object InnerAnimal{ var speed = 20 fun run(){ println("fun run running speed = $speed") } } } fun main() { Animal.run() Animal.speed = 50 Animal.run() }
- 伴随对象的初始化 伴随对象没有构造函数,可是能够给伴随对象添加 init{} 块,init块绘制实例化伴随对象所在类,或者第一次调用伴随对象的时候执行。
- 伴随对象的原理 以上面的Animal类为例子,通过编译器编译以后,speed变量就变成了 Animal类中的静态变量,伴随对象InnerAnimal 也是Animal类中的一个静态变量了。kotlin中所谓的伴随对象,其实就是一个普通的变量,可是这个变量有其不普通之处,那就是其变量名与伴随对象名称彻底相同,看起来像一个类名,一样,InnerAnimal变量的访问限定符包括static,所以能够 Animal.InnerAnimal 这样访问伴随对象
- 匿名类
open class Animal(val name:String) { open fun run(){ println("$name run........") } } fun main() { val a = object { init { println("init ..........") } //不像java中匿名类,只能调用父类(实现接口)的方法 //能够直接在 匿名类中 声明方法 fun print() { println("a.print()......") } } a.print() //匿名类 也能够实现接口 或者 继承某个基类 val dog = object :Animal("二哈"){ override fun run(){ println("dog $name is running ...") } } dog.run() }
init .......... a.print()...... dog 二哈 is running ...