Kotlin系统学习1

28 阅读7分钟

Kotlin系统学习1

  • Kotlin是基于JVM的编程语言,由JetBrains开发
  • Kotlin可被编译成Java字节码,也可以编译成JavaScript,方便运行在无JVM的设备上
  • Kotlin是Android开发的首选语言
  • 本节内容
    • 变量与常量
    • 注释
    • 数据类型
    • 控制语句
    • 操作符与操作符重载
    • 函数

变量与常量

  • var:变量声明关键字,声明可变变量,必须有初始值,或在init函数中赋值,或用lateinit声明非基本类型变量
  • val:不可修改变量声明关键字,声明不可变变量,必须赋值,或在init函数中赋值,不可用lateinit声明
// 指定类型并初始化
var a: Int = 10
a = 15 // 重新赋值

// 根据赋值推导类型
var b = 10
b = 15 // 重新赋值

// 不可这么定义变量,必须初始化,或使用lateinit声明后期再初始化
var c: Float // ×
var c: Float? = null // √
var c = 10f // √
lateinit var c: Data // √,lateinit不可以用于基本类型变量

// 不可变的变量定义
val d: Int = 100
d = 200 // ×,不可以重新赋值
d += 200 // ×,不可以重新赋值

Kotlin空安全

Kotlin中可声明一个变量是否可为空,使用可空变量增加程序安全性。

// 可空变量定义
var data: XxxData? = null

// 空安全访问,data为null时,id默认值为-1
val id = data?.id?:-1

Kotlin初始化

在Kotlin中变量要么直接赋初始值,要么设置可空变量默认null,要么声明后期初始化,要么声明延迟初始化,总之声明时必须初始化。

lateinit

后期初始化声明关键字。

  • 只可用于var声明的变量。
  • 不能用于可空变量。
  • 不可用于基本类型变量。例:Int、Float、Double不可以。String、Boolean可以。
  • 使用前需赋值否则会报错UninitializedPropertyAccessException
    • 检查是否初始化::tvView.isInitialized
// 声明
private lateinit var title: TextView

// 使用
if (::title.isInitialized) {    
}
lazy { }

延迟初始化,当变量第一次使用时初始化。

  • 只可用于val声明的不可变的变量。
private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

const

常量声明关键字,配合val一起使用。

  • 只能修饰val,不可修饰var
  • 声明:顶层声明、object类中、伴生对象中
const val FILTER = "."

注释

注释包括单行注释、多行注释、类或方法注释。

// 单行注释

/*
多行注释
 */
 
/**
 * 类或方法注释
 */

数据类型

数据类型是一门开发语言的基础构成,也是最基本的语法。

数值类型

严格的数值类型,在传参时,必须相同类型才可以,不同类型需转换,Int->Float必须转换为Float再传。

  • 字节类型:Byte,8位。
  • 短整型:Short,16位。
  • 整形:Int,32位。
  • 长整型:Long,64位。
  • 浮点型:Float,32位。
  • 双精度浮点型:Double,64位。
const val A: Byte = 0
const val B: Short = 1
const val C: Int = 2
const val D: Long = 3L
const val E: Float = 4f
const val F: Double = 5.0
进制数

Kotlin中的进制数包括二进制、十进制、十六进制,不支持八进制。

const val A = 0b00101001 // 二进制
const val B = 125 // 十进制
const val C = 0x0F // 十六进制
字面常量下划线
const val ONE_MILLION = 1_000_000 // 一百万
const val CREDIT_CARD_NUMBER = 1234_5678_9012_3456L // 信用卡号
const val SOCIAL_SECURITY_NUMBER  = 888_88_8888L // 社保卡号
const val HEX_BYTES = 0xFF_EC_DC_6E // 十六进制字节
const val BYTES = 0b11001100_10101010_01010101_00100100 // 字节
运算符
比较运算符
  • ==:值比较
  • ===:地址比较
位运算符

按位操作数值,仅适用于Int、Long类型。

  • shl(bits):有符号左移bits位
  • shr(bits):有符号右移bits位
  • ushr(bits):无符号右移bits位
  • and(bits):按位与
  • or(bits):按位或
  • xor(bits):按位异或
  • inv():按位取反
自增自减
  • inc():自增1
  • dec():自减1

布尔类型

布尔类型使用Boolean关键字声明,值只有true、false。

val flag: Boolean = false
逻辑运算符
  • 逻辑与:&&
  • 逻辑或:||
  • 逻辑非:!

字符型

字符变量使用Char声明,值用''表示,不可直接视为数字,但可显示转为数字。

转义字符
  • 制表符:\t
  • 换行符:\n
  • 退格键(键盘上的Back建):\b
  • 键盘上的Enter键:\r
  • 反斜杠:\
  • 单引号:'
  • 双引号:"
  • 美元符号:$,不转义在kotlin中表示变量的引用
  • 其他的任何字符请使用Unicode转义序列语法。例:'\uFF00'

字符串类型

字符串使用String关键字声明。可通过索引操作字符,也可使用for循环遍历字符。

  • 普通字符串:包含转义字符和不包含转义字符的用"..."括起来字符串
  • 三重引号字符串:包含任意字符由"""..."""括起来的字符串
  • 字符串引用使用$
val str = "string"
val first = str[0]
str.forEach { println(it) }
for (it in str) println(it)

// 字符串定义
val a = "abc"
val b = "$str abc"
val c = "abc\t abc"
val d = """
    abc
    def ghi
    jkl mn opq
    rst uvw xyz
""".trimIndent()
字符串操作
字符串查找
fun strOp() {
    val str = "Hello World!"
    val str1: String? = null

    // 获取第一个元素
    println("${str.first()}") // H
    println("${str1?.firstOrNull()}") // null
    println("${str.first { it == 'o' }}") // o
    println("${str.first { it == 'x' }}") // 不存在x,抛异常NoSuchElementException
    println("${str.firstOrNull { it == 'x' }}") // 不存在x,返回null

    // 获取最后一个元素
    println("${str.last()}") // !
    println("${str1?.lastOrNull()}") // null
    println("${str.last { it == 'o' }}") // o
    println("${str.lastOrNull { it == 'x' }}") // 不存在x,返回null
    println("${str.last { it == 'x' }}") // 不存在x,抛异常NoSuchElementException

    // 查找元素
    println("${str.find { it == 'o' }}") // 等价于firstOrNull()
    println("${str.findLast { it == 'W' }}") // 等价于lastOrNull()
    
    // 查找元素坐标
    println("${str.indexOf('o', 0)}") // 4
    println("${str.indexOf("World", 0)}") // 6
    println("${str.indexOfFirst { it == 'o' }}") // 4
    println("${str.indexOfLast { it == 'o' }}") // 7
    println("${str.lastIndexOf('o')}") // 7
    println("${str.lastIndexOf("World")}") // 6
}
字符串截取

字符串截取可使用方法:substring()、subSequence()。

  • Kotlin的substring()直接调用Java的substring()
fun subString() {
    val str = "Hello World!"

    // 字符串截取
    println(str.substring(6)) // World!,从下标6开始,到结束
    println(str.substring(0, 4)) // Hell,从下标0开始,取4个元素
    println(str.substring(IntRange(0, 4))) // Hello,从下标0开始,取到下标为4的元素,等价于调用substring(0, 4 + 1)
    println("${str.subSequence(0, 4)}") // Hell,从下标0开始,取4个元素
    println("${str.subSequence(IntRange(0, 4))}") // Hello,从下标0开始,取到下标为4的元素
}
字符串替换
  • replace():替换满足条件的字符
fun replaceString() {
    val str = "123 o 4 56 o 789"

    // 字符串替换
    println(str.replace('o', '-')) // 字符替换,123 - 4 56 - 789
    println(str.replace("o", ":")) // 字符串替换,123 : 4 56 : 789
    println(str.replace(Regex("[0-9]+"), "Hello")) // 正则替换,Hello o Hello Hello o Hello
    println(str.replace(Regex("[0-9]+")) { "World" }) // 正则替换,World o World World o World

}
  • replaceFirst():替换满足条件的第一个字符
fun replaceFirst() {
    val str = "123 o 4 56 o 789"

    // 字符串替换
    println(str.replaceFirst('o', '-')) // 满足条件第一个字符替换,123 - 4 56 o 789
    println(str.replaceFirst("789", "~")) // 满足条件第一个字符串替换,123 o 4 56 o ~
}
  • replaceBefore():从第一个匹配字符截取,并在前面追加字符
fun replaceBefore() {
    val str = "123 o 4 56 o 789"

    // 字符串替换
    println(str.replaceBefore('o', "AA")) // AAo 4 56 o 789
    println(str.replaceBefore("56", "kotlin")) // kotlin56 o 789
}
  • replaceBeforeLast():从最后一个匹配字符截取,并在前面追加字符
fun replaceBeforeLast() {
    val str = "123 o 4 56 o 789"

    // 字符串替换
    println(str.replaceBeforeLast('o', "AA")) // AAo 789
    println(str.replaceBeforeLast("56", "kotlin")) // kotlin56 o 789
}
  • replaceAfter():截取到第一个匹配的字符,并在其后追加字符
fun replaceAfter() {
    val str = "123 o 4 56 o 789"

    // 字符串替换
    println(str.replaceAfter('o', "AA")) // 123 oAA
    println(str.replaceAfter("56", "kotlin")) // 123 o 4 56kotlin
}
  • replaceAfterLast():截取到最后一个匹配的字符,并在其后追加字符
fun replaceAfterLast() {
    val str = "123 o 4 56 o 789"

    // 字符串替换
    println(str.replaceAfterLast('o', "AA")) // 123 o 4 56 oAA
    println(str.replaceAfterLast("56", "kotlin")) // 123 o 4 56kotlin
}
字符串分割

字符串分割方法:split()、splitToSequence()。

  • split():全量分割,返回List,占用大量内存。
  • splitToSequence():惰性分割,返回Sequence,几乎不占内存。
    • 按规则仅分割第一个元素,后续不执行:print("aa3b555d9c000j".splitToSequence("\d+".toRegex()).first())
fun split() {
    val str = "123 o 4 56 o 789"

    // 字符串分割
    println(str.split('o')) // [123 ,  4 56 ,  789]
    println(str.split(" ")) // [123, o, 4, 56, o, 789]

    val str2 = "123mine456kotlin789xyz"
    val str3 = "abc123mine456kotlin789xyz"
    println(str2.split(Regex("[0-9]+"))) // [, mine, kotlin, xyz]
    println(str3.split(Regex("[0-9]+"))) // [abc, mine, kotlin, xyz]
    println(str2.split(Pattern.compile("[0-9]+"))) // [, mine, kotlin, xyz]
    println(str3.split(Pattern.compile("[0-9]+"))) // [abc, mine, kotlin, xyz]
    
    // 单字符分割符
    val str4 = "Kotlin,Java,C,C++,Javascript"
    str4.splitToSequence(",").forEach { print("$it ") } // Kotlin Java C C++ Javascript

    // 多字符分割符
    val str5 = "Kotlin,Java-C|C++|Javascript"
    str5.splitToSequence(",", "-", "|").forEach { print("$it ") } // Kotlin Java C C++ Javascript

    val str6 = "aa3b555d9c000j"
    str6.splitToSequence("\d+".toRegex()).forEach { print("$it ") } // aa b d c j
    
    // 仅取前三个元素,不会截取整个字符串,生成前三个后停止
    str6.splitToSequence("\d+".toRegex()).take(3).forEach { print("$it ") } // aa b d
    str6.splitToSequence("\d+".toRegex()).take(3).toList().forEach { print("$it ") } // aa b d
}
其他操作
fun other() {
    val str: String? = "Hello World!"

    // 获取字符串长度
    println(str?.length) // 12
    println(str?.count()) // 12

    // 统计重复字符个数
    println(str?.count { it == 'l' }) // 3

    // 验空
    println(str?.isEmpty()) // false,判断length是否为0
    println(str?.isNotEmpty()) // true,判断length是否大于0
    println(str?.isNullOrEmpty()) // false,判断字符串是否为null,或length是否为0
    println(str?.isBlank()) // false,判断length是否为0,判断空格数是否等于当前length
    println(str?.isNotBlank()) // true,对isBlank()取反
    println(str?.isNullOrBlank()) // false,判断字符串是否为null,或调用isBlank()

    // 字符串连接
    println(str.plus(" Kotlin")) // Hello World! Kotlin
    println(str.plus(123)) // Hello World!123
    println(str + true) // Hello World!true
    
    // 字符串反转
    println(str.reversed()) // !dlroW olleH
    
    // 判断字符串起始
    println(str.startsWith('H')) // true
    println(str.startsWith("Hello")) // true
    println(str.startsWith("World", 6)) // true,第6个字符是否以World开始
    println(str.endsWith('!')) // true
    println(str.endsWith("rld!")) // true
}

数组类型

数组由Array表示,创建方式:arrayOf、arrayOfNulls、工厂函数(Array)。

  • 原始类型数组
    • 字节型数组:ByteArray(2)
    • 短整型数组:ShortArray(2)
    • 整型数组:IntArray(2)
    • 长整型数组:LongArray(2)
    • 布尔型数组:BooleanArray(2)
    • 字符型数组:CharArray(2)
    • 浮点型数组:FloatArray(2)
    • 双精度浮点型数组:DoubleArray(2)
// 创建一个数组,参数是一个可变参数的泛型对象
val a = arrayOf("abc", "xyz")

// 创建一个指定数据类型且可以为空元素的给定元素个数的数组
val b = arrayOfNulls<String>(3) // 默认3个值都为null
b[0] = "abc"
b[1] = null
b[2] = "xyz"

// 工厂函数Array()创建数组,它使用数组大小和返回给定其索引的每个数组元素的初始值的函数。
// Array() => 第一个参数表示数组元素的个数,第二个参数则为使用其元素下标组成的表达式
val c = Array(5) { index -> index }
val d = Array(5) { index -> "string $index" }

val e = intArrayOf(0, 1, 2, 3, 4, 5)
val f = charArrayOf('a', 'b', 'c', 'd', '1', '2')

可空类型

可空类型定义格式以String类型为例var a: String? = null,即在变量类型后加?。加了?的var变量才可被赋值为null。

  • 可空类型使用方式
    • 使用if...else...判断
    • 使用?.判断
  • 可空类型操作符
    • ?::可空类型赋默认值
    • !!:强制指定可空对象为非空,当对象为空时抛出空指针异常NullPointerException
    • as?:类型安全转换,as操作符属于强制转换,类型不匹配会抛出类型转换异常,而as?会返回null,而不抛出异常。
var a: String? = null
var b: String = null // ×

// 可空类型使用方式
var a: String? = null
if (a == null) {
} else {
}

val i = a?.length // a为null时,i为null
val i = a?.length?:-1 // a为null时,i为-1
val i = a!!.length // a为null时,抛出空指针异常

print("${"abc" as Int}") // 抛出ClassCastException异常
print("${"abc" as? Int}") // null

控制语句

if语句

kotlin中if语句不仅可以作为条件判断,也可以作为块用于返回值

if (flag) true else false
val a = if (flag) "a" else "A" // a = a 或 a = A

for语句

  • for循环提供迭代器用来遍历任何东西
  • for循环数组被编译为一个基于索引的循环,不会创建迭代器对象
// 递增[0, 5)
for (i in 0 until 5) {
    // i >= 0 && i < 5
    // 0、1、2、3、4
    println("$i")
}

// 递增[0, 5]
for (i in 0 .. 5) {
    // i >= 0 && i <= 5
    // 0、1、2、3、4
    println("$i")
}

// 递减
for (i in 5 downTo 0) {
    // i >= 0 && i < 5
    // 5、4、3、2、1、0
    println("$i")
}

// 设置步长
for (i in 0 .. 5 step 2) {
    // i >= 0 && i <= 5,每次+2
    // 0、2、4
    println("$i")
}

// 遍历
for (c in "abc_def_ghi") {
    println("$c")
}

val values = intArrayOf(0, 1, 2, 3, 4, 5)
for (value in values) {
    println("value: $value")
}
for (i in values.indices) {
    println("index: $i, value: ${values[i]}")
}
for ((i, value) in values.withIndex()) {
    println("index: $i, value: $value")
}

while语句、do...while语句

循环语句,二者区别是后者最少执行一次

// 迭代器遍历
val hybridArray = arrayOf(0, 'a', true, "abc", 1f, 1.0)
val iterator: Iterator<Any> = hybridArray.iterator()
while (iterator.hasNext()) {
    println("value: ${iterator.next()}")
}


// while循环求和
var i = 0
var sum = 0
while (i < 10) {
    sum += i
    i++
}

// do...while循环求和
var i = 0
var sum = 0
do {
    sum += i
    i++
} while (i < 10)

when语句

分支语句

// 使用方式1:分支匹配
val flag = true // 或flag等于其他值,分支改为对应的匹配规则即可
when (flag) {
    true -> {}
    false -> {}
}

// 使用方式2:合并分支
val flag = 1
when (flag) {
    1, 2, 3, 4 -> {} // 多条分支处理一致时,使用逗号合并分支
    else -> {}
}

// 使用方式3:表达式
val flag = 5
when (flag > 3) {
    true -> {}
    false -> {}
}

// 使用方式4:值是否属于某一集合
val flag = arrayOf(1, 2, 3, 4, 5)
when (3) {
    in flag -> {}
    !in 7 .. 10 -> {}
    else -> {}
}

// 使用方式5:类型判断
val flag = "abc"
when (flag) {
    is String -> {} // 或!is String
    else -> {}
}

// 使用方式6:无表达式
when {
    3 > 5 -> {}
    1 is Int -> {}
    else -> {}
}

return、break、continue语句

跳转语句。

  • return:结束封闭函数或匿名函数返回
  • break:终止最近的循环体
  • continue:跳至循环的下一个步骤
// return
fun returnFun(flag: Boolean) {
    if (!flag) {
        return
    }
    println("flag: $flag")
}

// break
fun breakFun() {
    var count = 1
    for (i in 0 .. 10) {
        count++
        if (count > 5) {
            break
        }
    }
    print(count)
}

// continue
fun continueFun() {
    var x = 0
    for (i in 0 .. 10) {
        if (i == 5) {
            x = i
            continue
        }
    }
    print(x)
}

操作符与操作符重载

一元操作符

只有一个操作数

  • +:正号,+5 == 5.unaryPlus()
  • -:负号,-5 == 5.unaryMinus()
  • !:取反,!true == true.not()
  • ++:自增
    • a++:先计算,后自增
    • ++a:先自增,后计算
  • --:自减
    • a--:先计算,后自减
    • --a:先自减,后计算
var a = 10
var b = 10
var c = 10
var d = 10
    
// a++=10, ++b=11, c--=10, --d=9
println("a++=${a++}, ++b=${++b}, c--=${c--}, --d=${--d}")

// 重载
var a = 10
var b = 10
var c = 10
var d = 10

a.also { a.inc() } // a++
b.inc().also { b = it } // ++b
c.also { c.dec() } // c--
d.dec().also { d = it } // --d

// a++=10, ++b=11, c--=10, --d=9
println("a++=${a}, ++b=${b}, c--=${c}, --d=${d}")

二元操作符

操作数大于等于两个

  • +:加法,a+b == a.plus(b)
  • -:减法,a-b == a.minus(b)
  • *:乘法,a*b == a.times(b)
  • /:除法,a/b == a.div(b)
  • %:取余,a%b == a.mod(b) == a.rem(b)
  • ..:范围,a..b == a.rangeTo(b)
  • +=:加等,a+=b -> a=a+b -> a=a.plus(b)
  • -=:减等,a-=b -> a=a-b -> a=a.minus(b)
  • =:乘等,a=b -> a=a*b -> a=a.times(b)
  • /=:除等,a/=b -> a=a/b -> a=a.div(b)
  • %=:余等,a%=b -> a=a%b -> a=a.rem(b)

函数

函数使用fun关键字声明,格式:private fun funName(params: Int, ...): Int { }

  • 默认修饰符为public,还可以使用private仅内部使用
  • ()中为参数,参数可有可无
  • {}中为函数体,在单函数体作为返回值时可省略大括号fun funName(): Boolean = true
  • 无返回值时可省略返回值类型fun funName(): Unit { }fun funName() { }
fun funName(a: Int): Int {
    return a * a
}

// 或
fun funName(a: Int): Int = a * a

函数参数

  • 无参数
  • 有参数
  • 默认参数,参数有默认值
  • 命名参数,传参时显示指定参数名
  • 可变参数,参数个数不定,使用vararg修饰变量fun funName(vararg a: Int) { }
    • vararg修饰的变量相当于一个固定类型的数组
    • 集合作为参数时需使用伸展操作符*
// 无参数
private fun funName() { }

// 有参数
private fun funName(a: Int, b: Int): Int = a + b

// 默认参数,参数有默认值
private fun funName(a: Int = 1, b: Int = 2): Int = a + b
funName(3, 5) // 使用3、5代替a和b
funName() // 使用函数默认值

// 命名参数,传参时显示指定参数名
private fun funName(a: Int = 1, b: Int = 2): Int = a + b
funName(b = 3) // a使用默认值,b使用命名方式指定为3

// 可变参数,参数个数不定
private fun funName(vararg a: Int) {
    // 获取元素
    println(a[0]) // 1
    println(a.component1()) // 1
    // ...1-5对应0-4下标数据
    println(a.component5()) // 5

    println("${a.map { print(it) }}") // 12345[kotlin.Unit, kotlin.Unit, kotlin.Unit, kotlin.Unit, kotlin.Unit]
    println("${a.filter { it % 2 == 0 }}") // [2, 4]
    println("${a.sortedBy { it }}") // 按指定字段升序排序[1, 2, 3, 4, 5]
}
funName(1, 2, 3, 4, 5)

// 集合需使用伸展操作符*
val arr = intArrayOf(1, 2, 3, 4, 5)
funName(*arr)

单表达式函数

函数具有返回值并且函数体只有一行。

private fun funName(a: Int, b: Int) = a + b
private fun funName(a: Int, b: Int): Int = a + b