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 ...