[TOC]
kotlin 基礎(chǔ)
java 中有 main()
方法作為程序的入口,那么在 kotlin 里邊也是類似的,也是提供了一個(gè)入口函數(shù),如下:
fun main(args: Array<String>) {
println("Hello World")
}
在 kotlin 定義一個(gè)函數(shù)(或者稱之為方法)是以fun
開頭的,這有點(diǎn)類似于 JS 里邊的 function 似的.其中的args: Array<String>
指的是這個(gè)函數(shù)的參數(shù),在 kotlin 中,參數(shù)類型是放在后邊而參數(shù)名是在前邊的,這個(gè)跟 java 中是正好相反的.
println(message)
就是 kotlin 中的輸出語句,內(nèi)部實(shí)際調(diào)用了System.out.println(message)
.
基本語法
注釋
// 這是單行注釋
/*
* 這是多行注釋
*/
/**
* 這是文檔注釋
**/
變量
kotlin 中的變量,分為可變變量
和不可變變量
.
可變變量用var
修飾,一次賦值,支持多洗修改;不可變變量用val
修飾(等價(jià)于 java 中用 final 關(guān)鍵字修飾的變量),一次賦值,不可更改.
kotlin 聲明變量的時(shí)候,類型是在后邊聲明的.
舉例:
var a: String = "a" // 聲明一個(gè) String 類型的變量 a
val b: String = "b" // 聲明一個(gè) String 類型的常量 b,不可重新賦值
a = "aa" // 沒問題
b = "bb" // 會(huì)報(bào)錯(cuò)
變量的數(shù)據(jù)類型
kotlin 中的數(shù)據(jù)類型,分為數(shù)值型,字符型,布爾型,數(shù)組性,字符串型等
數(shù)值類型
Byte,Float,Double,Int,Long,Short
布爾類型
Boolean
字符類型
Char
字符串類型
String
數(shù)組類型
常規(guī)使用是Array
,但是針對(duì)某些特殊的比如 Int,Boolean 的數(shù)組,提供了特殊的格式IntArray
,BooleanArray
舉例說明:
var byte: Byte
var float: Float = 1.0F
var double: Double = 2.0
var int: Int = 3
var long: Long = 33
var short: Short = 4
var boolean: Boolean = false
var char: Char = 'c'
var string: String = "string"
var intArray: IntArray = intArrayOf(1, 2, 3)
var booleanArray: BooleanArray = booleanArrayOf(false, true)
var stringArray: Array<String> = arrayOf("a", "b", "c")
運(yùn)算符
算術(shù)運(yùn)算符,賦值運(yùn)算符,比較運(yùn)算符,邏輯運(yùn)算符.跟 java 中沒什么區(qū)別
字符串
定義同 java 中的字符串,定義以及普通用法一樣一樣的...
val s = "Hello Kotlin" // 定義一個(gè)字符串
println(s)
println(s[0]) // 根據(jù)索引值來獲取對(duì)應(yīng)位置上的字符
for (c in s) { // 對(duì)字符串進(jìn)行遍歷
print("$c,")
}
println()
// 同時(shí),kotlin 標(biāo)準(zhǔn)庫中也提供了對(duì)應(yīng)的字符串遍歷的方法。
s.forEach {
print("$it,")
}
kotlin 中一些增加的方法.
字符串查找
println(s.first()) // 獲取第一個(gè)字符
println(s.last()) // 獲取最后一個(gè)字符
println(s.get(1)) // 獲取索引值為 1 的字符
println(s.lastIndexOf("l")) // 獲取指定字符最有一次出現(xiàn)的索引值
字符串截取
方法同 java 中的 subString()
字符串替換
同 java 中的 replace()
模板表達(dá)式
java 中要使用一個(gè)變量,需要用+號(hào)拼起來用,如下
String a = "a";
String ab = a + "b";
但是 kotlin 中可以使用$
來實(shí)現(xiàn)模板化,可以直接在雙引號(hào)之間引入你所需要的變量.單個(gè)引用可以省略掉{}
val a = "a"
val b = "${a}b"
println("b = $b") // b = ab
選擇結(jié)構(gòu)語句
if 語句和 when(等同于 java 中的 switch/case) 語句
if 語句
if 語句的使用,跟 java 中一模一樣
// if 語句
val point = 60
if (point < 60) {
println("不及格")
} else if (60 < point && point < 90) {
println("良好")
} else {
println("優(yōu)秀")
}
三元運(yùn)算符
kotlin 中不存在三元運(yùn)算符,但是可以使用如下實(shí)現(xiàn):
val result = if (point < 60) "淘汰" else "過關(guān)"
when 語句
when 語句其實(shí)就是 java 中的 switch/case 語句.
// when 語句
val season = 3
val seasonResult = when (season) {
1 -> "春季"
3 -> "春季"
6 -> "夏季"
12 -> "冬季"
else -> "未知季節(jié)"
}
示例代碼中,將 when 語句的返回值賦給了一個(gè)變量,然后進(jìn)行輸出,可以看到, ->
符號(hào)前邊的就是各種 case,后邊的就是需要匹配的內(nèi)容.
同時(shí)也支持多個(gè) case 匹配,比如 1,2,3 都是春季,那么可以采用如下寫法:
val seasonResult = when (season) {
1, 2, 3 -> "春季"
4, 5, 6 -> "夏季"
else -> "其他季節(jié)"
}
循環(huán)結(jié)構(gòu)語句
while,do..while 等循環(huán)結(jié)構(gòu)都跟 java 中一樣,使用方法也是一樣的.主要學(xué)一下 for 循環(huán).
kotlin 中的 for 循環(huán)是這樣的.
for(循環(huán)對(duì)象 in 循環(huán)條件) {
執(zhí)行語句
}
其中的in
其實(shí)是 kotlin 中的范圍,
普通 for 循環(huán)
下邊的代碼就是循環(huán) 4 次,輸出 4 次日志.其中的in
指的是范圍,1..4
中的..
指的是區(qū)間
,等價(jià)于大于等于 1,小于等于 4
,所以最后的實(shí)際輸出是 4 次.
// for 循環(huán)
for (i in 1..4) {
println("string--->$i")
}
forEach(),forEachIndexed()
kotlin 中還提供了兩個(gè)增強(qiáng)類型的for 循環(huán),主要針對(duì)數(shù)組和集合,但是所有對(duì)象都可用.
forEach{}: 接收一個(gè)閉包參數(shù)
val forArray = arrayOf("a", "b", "c")
forArray.forEach {
println("forEach----$it")
}
forEachIndexed{}: 兩個(gè)參數(shù),第一個(gè)是角標(biāo);第二個(gè)是元素
forArray.forEachIndexed { index, it ->
println("forEachIndexed----index=$index it=$it")
}
區(qū)間
區(qū)間指的是一類數(shù)據(jù)的集合,比如在數(shù)學(xué)中的[1,3],[1,3)等.
rangeTo(Int) & ..
rangeTo(Int)函數(shù)構(gòu)成了區(qū)間表達(dá),可以用..
操作符來簡寫
// 區(qū)間
val qujian = 1.rangeTo(3) // 等價(jià)于 1 <= qujian <= 3
for (index in qujian) {
println("區(qū)間的值:$index")
}
此處可以把rangeTo()
替換成..
操作符來實(shí)現(xiàn),效果一致
until 操作符
假如我要實(shí)現(xiàn) 1 <= index < 3 ,kotlin 里邊怎么寫?
在 kotlin 中,有個(gè) until
操作符,效果可以實(shí)現(xiàn)右邊開區(qū)間的效果,如下:
// 輸出結(jié)果只有 1,2 兩個(gè)值,因?yàn)橛疫叢话?3
for (index in 1 until 3) {
println("until的值:$index")
}
downTo(Int)
要是想實(shí)現(xiàn)逆序遍歷區(qū)間呢?
kotlin 里邊提供了downTo(Int)
方便的實(shí)現(xiàn)逆序區(qū)間.
// 輸出:3,2,1
for (index in 3.downTo(1)) {
println("區(qū)間的值:$index")
}
步長 step
前邊說的區(qū)間,遍歷的時(shí)候都是步長是 1 的,也就是保證輸出是 1,2,3... 假如我要實(shí)現(xiàn)輸出 1,3,5...呢?
kotlin 里邊提供了step
關(guān)鍵字,支持設(shè)置步長.使用很簡單,就是在循環(huán)條件之后加上step
關(guān)鍵字,然后指定你需要的步長就行了,比如下邊是保證了輸出奇數(shù)...
// 輸出 1,3,5,7,9
for (index in 1..10 step 2) {
println("step = $index")
}
數(shù)組
kotlin 中的數(shù)組,用 Array 表示,通過泛型來控制數(shù)組中的元素類型.
// 數(shù)組
val array = arrayOf<String>("a", "b", "c")
// 通過[]直接獲取索引值處的元素
println("索引值為 1 的元素是:${array[1]}")
println("索引值為 1 的元素是:${array.get(1)}")
println("數(shù)組的長度是:${array.size}")
數(shù)組常見操作
遍歷
kotlin 中常見的遍歷方法有 for 循環(huán)遍歷和 forEach 遍歷,同時(shí)都提供了按照索引值遍歷的方案.
// 遍歷
array.forEach {
println("數(shù)組元素:$it")
}
array.forEachIndexed { index, content ->
println("數(shù)組元素:index=$index content=$content")
}
for (content in array) {
println("數(shù)組元素(for 循環(huán)):$content")
}
for ((index, content) in array.withIndex()) {
println("數(shù)組元素(withIndex):index=$index content=$content")
}
修改數(shù)組的值
kotlin 的數(shù)組中提供了set(index, value)
方法用來修改指定索引值上的值,同時(shí)可以簡寫為[]
array.set(1, "bb")
array[1] = "bb"
獲取元素的索引值
通過indexOf(value)
可以獲得指定元素在數(shù)組中的索引值.
println("值為 b 的索引值是:${array.indexOf("b")}")
變量類型轉(zhuǎn)換
kotlin 中的類型轉(zhuǎn)換,包括智能類型轉(zhuǎn)換和強(qiáng)制類型轉(zhuǎn)換.
類型檢查
kotlin 中可以采用is
或者!is
來判斷是否是某種類型的.
// 類型判斷
val typeBoolean: Boolean = false
println("typeBoolean 的類型是:${typeBoolean is Boolean}")
智能類型轉(zhuǎn)換
在 kotlin 中,經(jīng)過is
類型判斷之后,kotlin 會(huì)智能的轉(zhuǎn)換成當(dāng)前判斷的類型
強(qiáng)制類型轉(zhuǎn)換
在 kotlin 中可以采用as
關(guān)鍵字實(shí)現(xiàn)強(qiáng)制類型轉(zhuǎn)換,同時(shí)可以使用as?
進(jìn)行安全的類型轉(zhuǎn)換.
val aStr = "aaaa"
val bString = aStr as String
println("bString 的類型是:${bString.javaClass}") // class java.lang.String
接下來我嘗試將一個(gè) Int 類型的轉(zhuǎn)換成 String 類型的試試
val aInt = 1
val bInt = aInt as String
println(bInt)
此時(shí)會(huì)報(bào)錯(cuò):
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at com.yanftch.review.kotlinbasic.KtKt.main(Kt.kt:137)
這就是類轉(zhuǎn)換異常的報(bào)錯(cuò),那么在 kotlin 中該怎么避免你呢? 這就是下邊說的as?
關(guān)鍵字了,他可以在上述情況下避免拋出異常.
所以上邊的代碼,如果采用 as? 的話,最后會(huì)正常輸出,但是輸出的值是 null
val aInt = 1
val bInt = aInt as? String
println(bInt) // 輸出 null
空值處理
空指針異常是最常見的開發(fā)異常,那么 kotlin 中是怎么處理空指針異常呢?
可空類型變量 ?
在 kotlin 可以通過?
指定一個(gè)變量是可空的變量,可空變量可以賦值為 null,
var name: String // 不可空變量
var age: Int? // 可空變量
上邊的代碼中,制定了 name 為不可空變量,那么如果給賦值 null 就會(huì)報(bào)錯(cuò),相反,age 變量指定了為可空變量,那么就可以賦值為 null.
安全調(diào)用符 ?.
當(dāng)一個(gè)變量為可空的時(shí)候,我們?cè)谑褂玫臅r(shí)候,需要對(duì)他做空判斷之后再用,kotlin 中可以直接使用?.
操作符進(jìn)行可空類型的變量的調(diào)用,也不會(huì)出現(xiàn)任何問題.
val name: String? = null
print(name?.toString())
Elvis 操作符 ?:
采用 ?. 調(diào)用的時(shí)候,如果調(diào)用者是為空的,那么返回地同樣也是空, 如果我不想要 null 而是想要一個(gè)默認(rèn)值呢?那么很簡單,可以采用?:
操作符, 他的用處是:調(diào)用者如果不為空,那么就返回該有的值(也就是?:前邊的值);調(diào)用者為空,那么就返回設(shè)置好的默認(rèn)值(也就是?:后邊的值)
val name: String? = null
val nameResult = name ?: "default"
println("nameResult 的值是: $nameResult")
name 為空的時(shí)候,返回的值就是 "default",不為空,就返回 name 的實(shí)際值.
函數(shù)/方法
kotlin 中的函數(shù),也就是方法,用處跟java 中一樣,但是增加了一些自己的特性.
函數(shù)的定義
kotlin 中的函數(shù),需要通過關(guān)鍵字fun
來定義. 通過一個(gè)簡單的函數(shù)來學(xué)習(xí)
fun method(a: Int, b: Int): Int {
val sum = a + b
return sum
}
- fun: 函數(shù)的命名關(guān)鍵字
- method: 函數(shù)的名字
- (a: Int, b: Int): 表示的是兩個(gè)參數(shù),其中分號(hào)前邊是參數(shù)名稱,后邊是參數(shù)類型,多個(gè)參數(shù)之間用逗號(hào)分開
- Int: 大括號(hào)前邊的這個(gè) Int 表示的是當(dāng)前函數(shù)的返回值類型.
- val sum = a + b: 函數(shù)體
- return sum: 表示的是函數(shù)的返回值.
如果一個(gè)函數(shù)不返回任何類型,那么實(shí)際是返回的Unit
類似 java 中的void
.可省略不寫.
函數(shù)的類型
函數(shù)整體分為:有(無)參函數(shù); 有(無)返回值函數(shù)
kotlin 中,如果函數(shù)體只有一行,那么可以省略返回值類型以及大括號(hào),用一行代碼來實(shí)現(xiàn),如下:
fun getAge() = 18
函數(shù)的參數(shù)
函數(shù)的參數(shù),主要將具名參數(shù)和默認(rèn)參數(shù).
具名參數(shù)
就是指的在使用的時(shí)候,顯示的表明參數(shù)的名稱,這樣可以忽略參數(shù)的順序
// 具名函數(shù)
fun method2(name: String, age: Int) {
println("name=$name, age=$age")
}
// 調(diào)用
method2(age = 18, name = "tom")
默認(rèn)參數(shù)
java 中可以通過方法重載,實(shí)現(xiàn)同名函數(shù),不同參數(shù),在 kotlin 中可以使用一個(gè)函數(shù)來實(shí)現(xiàn)類似重載的作用.
就是在函數(shù)的定義的時(shí)候,給某些指定的參數(shù)聲明默認(rèn)值.比如上邊的函數(shù),可以給 age 指定默認(rèn)為 20,那么就算是后續(xù)調(diào)用函數(shù)的時(shí)候,不設(shè)置 age 的值,他的輸出也是 20
fun method2(name: String, age: Int = 20) {
println("name=$name, age=$age")
}
// 調(diào)用
method2(name = "tom") // 輸出:name=tom, age=20
面向?qū)ο?/h3>
會(huì) java 的,面向?qū)ο筮€需要說嗎?~~~
類與對(duì)象
類定義
聲明一個(gè)類,也是使用class
關(guān)鍵字
class User1 constructor(name: String) {
private var mName: String? = null
private var mAge: Int = 0
fun user1Method() {
println("name=$mName, age=$mAge")
}
}
mName, mAge: 類屬性或者類成員變量
user1Method: 類方法
對(duì)象創(chuàng)建
在 java 中需要 通過 new
關(guān)鍵字來實(shí)例化一個(gè)對(duì)象,但是在 kotlin 可以省略掉他.
val user1 = User1()
然后可以對(duì)應(yīng)的調(diào)用類的成員變量和函數(shù)
user1.mAge
user1.user1Method()
構(gòu)造函數(shù)
構(gòu)造函數(shù)可以對(duì)類中的一些變量進(jìn)行初始化操作賦值. kotlin 可以直接在類定義的時(shí)候指定構(gòu)造函數(shù),稱之為主構(gòu)造,然后可以在內(nèi)部創(chuàng)建次要構(gòu)造.
主構(gòu)造:
用constructor
修飾,作用于類頭和類名之間,如果沒有參數(shù)的話可以省略.
class User1 constructor(name: String) {}
如上就是通過主構(gòu)造函數(shù)給當(dāng)前類傳遞了一個(gè)參數(shù).
name 參數(shù)的使用的話,就是可以直接在 kotlin 中的init{}
代碼塊中進(jìn)行處理.
class User1 constructor(name: String) {
private var mName: String? = null
init {
this.mName = name
}
}
次要構(gòu)造
一個(gè)類可以有多個(gè)次要構(gòu)造函數(shù).
也需要用那個(gè)關(guān)鍵字修飾
// 定義次構(gòu)造函數(shù),傳遞兩個(gè)參數(shù),并調(diào)用主構(gòu)造函數(shù)
constructor(name: String, age: Int) : this(name) {
this.mAge = age
}
this 關(guān)鍵字
作用同 java 中
類繼承
讓子類擁有父類的特性.java 中通過extends
關(guān)鍵字實(shí)現(xiàn)子類繼承父類,kotlin 中通過:
來實(shí)現(xiàn).
kotlin 中所有的類默認(rèn)是final 的,不能被繼承,所以你需要對(duì)你要繼承的類用 open 關(guān)鍵字修飾
class User2 constructor(name: String) : User1(name) {
}
// User1 只有一個(gè)構(gòu)造函數(shù),傳參 name,那么子類繼承時(shí)需要傳.
- 單繼承:一個(gè)類只能有一個(gè)直接父類
- 支持多層繼承
函數(shù)重寫
方法的重寫,存在于子類和父類之間,同 java 中,需要override
修飾.
Any 和 Object
在 java 中,所有類的根本父類是 Object, 但是 Kotlin 中的根本父類是Any
, Any 在運(yùn)行時(shí),會(huì)自動(dòng)映射成 Object 對(duì)象.
常見類
嵌套類
kotlin 中,一個(gè)類內(nèi)部再聲明一個(gè)類, 成為嵌套類,嵌套類默認(rèn)不持有外部的引用,因此不能訪問到外部的變量或者函數(shù),如果希望可以引用,需要將內(nèi)部的類,用inner
關(guān)鍵字修飾.
如下代碼中, 在嵌套類里邊會(huì)報(bào)錯(cuò),提示找不到那兩個(gè)參數(shù).
class Outer {
private var mName: String? = null
private var mAge: Int = 0
class Inner {
init {
println("name=$mName, age=$mAge")
}
}
}
通過字節(jié)碼反編成 java 文件,發(fā)現(xiàn),不用inner
修飾的內(nèi)部的類,其實(shí)等同于 java 中的靜態(tài)內(nèi)部類.加了修飾之后,就是普通的內(nèi)部類.
數(shù)據(jù)類
在java 中,定義一個(gè)類,要想外部訪問,需要提供 set/get 方法,但是在 kotlin 中,只需要在類聲明的時(shí)候在class 前邊加上data
關(guān)鍵字修飾就行了, 內(nèi)部自動(dòng)提供 set/get 方法.
data class DataClassModel(var name: String, var age: Int)
// 其他地方調(diào)用
println("${dataClassModel.age}, ${dataClassModel.name}")
DataClassModel 實(shí)例化之后,可以直接通過.
來獲取對(duì)應(yīng)的屬性.