本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

Kotlin 速查 — 简明参考

Kotlin 2.0 语法、空安全、协程与最常用标准库速查手册,覆盖约 80% 日常场景。

Kt

Kotlin Kotlin 2.0

JVM (kotlinc) / Multiplatform · OO · 函数式 · 多平台 · 静态 · 强类型 · 推断

学习路径

先学会 kotlinc 编译运行与 fun main → 掌握变量、类型与流程控制 → 理解空安全(可空类型 ?、安全调用 ?.、Elvis ?:)与 data class → 用 lambda、扩展函数与集合函数式操作 → 深入类、接口与泛型 → 用 try / runCatching 处理错误 → 用协程与 Flow 写并发 → 再按需学网络、时间、正则与 Gradle 构建。FAQ 节适合回头避坑。

1.Hello World 与构建环境

编译运行 Kotlin 程序:kotlinc 工具链、脚本、包结构与命令行参数。

最小程序

每个 Kotlin 程序以顶层 fun main() 为入口,println 输出到标准输出。Kotlin 2.0 支持无参数 main。

1
2
3
4
5
6
7
8
fun main() {
println("Hello, world!")
}
// 保存为 Main.kt,用 kotlinc 编译
// 顶层函数无需类包裹
// Kotlin 2.0 的 main 可无参数
// 也可带参数:fun main(args: Array<String>)
// 每行代码结尾无需分号

运行与构建

kotlinc 把源码编译成 JVM 字节码,java -jar 运行。Gradle 是 Kotlin 项目的主流构建工具。

1
2
3
4
5
6
7
8
9
10
11
// 编译为可执行 jar:
// kotlinc Main.kt -include-runtime -d hello.jar
// 运行:
// java -jar hello.jar
// 编译多个文件:
// kotlinc a.kt b.kt -include-runtime -d app.jar
// 仅编译为字节码目录:
// kotlinc a.kt -d out
// 直接运行源码(免编译):
// kotlin Main.kt
// 查看版本:kotlinc -version

Kotlin 脚本

.kts 文件可直接运行,适合小工具与自动化。脚本不需要 main 函数,顶层语句顺序执行。

1
2
3
4
5
6
7
8
9
10
11
// hello.kts
val name = "Kotlin"
println("Hello, $name!")
// 运行:kotlin hello.kts
// 脚本允许顶层表达式:
val items = listOf(1, 2, 3)
println(items.sum())
// 适合原型与日常小任务
// 项目代码仍用编译型 main
// 脚本内可直接调用第三方 jar:
// kotlin -classpath lib.jar app.kts

包与导入

package 声明文件所属包,import 引入其他包类型。import 未使用不报错,与 Java 不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.example.demo
import kotlin.math.sqrt
import kotlin.math.max as mx
import java.util.UUID
fun main() {
println(sqrt(16.0))
println(mx(1, 2))
println(UUID.randomUUID())
}
// 通配导入:
// import kotlin.math.*
// 顶层函数与类默认 public
// import 未使用不报错

命令行参数

main 的参数 Array<String> 接收命令行参数,args[0] 是第一个参数。参数总是字符串需自行转换。

1
2
3
4
5
6
7
8
9
10
11
fun main(args: Array<String>) {
println("共 ${args.size} 个参数")
args.forEach { println(it) }
// 参数总是字符串,需自行转换:
val port = args.firstOrNull()
?.toIntOrNull() ?: 8080
println("端口: $port")
}
// 运行:kotlin Main.kt a b c
// args = [a, b, c]
// 健壮解析用 toIntOrNull 避免抛异常

输出与格式化

println 输出并换行,print 不换行。字符串模板 ${} 内嵌表达式,是最常用的格式化方式。

1
2
3
4
5
6
7
8
9
10
11
12
fun main() {
val name = "Kotlin"
val year = 2026
println("Hello, $name!") // 简单变量
println("今年是 ${year - 2025} 周年") // 表达式
print("不换行")
val pi = 3.14159
println("Pi = %.2f".format(pi)) // Pi = 3.14
println("%5d".format(42)) // " 42"
// printf 风格(Java 互通):
System.out.printf("name=%s%n", name)
}

退出码

exitProcess 立即终止程序并设置退出码,0 表示成功,非 0 表示失败。库代码不要调用它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import kotlin.system.exitProcess
fun main(args: Array<String>) {
if (args.isEmpty()) {
println("用法: app <name>")
exitProcess(1)
}
println("Hello, ${args[0]}")
exitProcess(0)
}
// exitProcess 立即退出,不执行 finally
// 0 成功,非 0 失败
// 与 Java 的 System.exit 等价
// 库代码不要调用 exitProcess

环境搭建

IntelliJ IDEA 社区版内置 Kotlin 支持,Android Studio 亦然。命令行则用 kotlinc 与 Gradle。

1
2
3
4
5
6
7
8
9
10
// IntelliJ IDEA 社区版(免费)
// 新建项目选 Kotlin/JVM
// Android Studio 内置 Kotlin 支持
// 命令行工具:kotlinc + Gradle
// 验证安装:
// kotlinc -version
// gradle -version
// 国内建议配置阿里云 Maven 镜像
// 初学无需 Android,直接跑 JVM 项目
// 多平台项目需额外 Kotlin Multiplatform 插件

2.变量与常量

变量声明、val/var、常量、类型推断、解构与作用域。

val 与 var

val 声明只读引用,var 声明可变引用。优先使用 val,编译器会提示可改为 val 的 var。

1
2
3
4
5
6
7
8
9
10
11
12
fun main() {
val pi = 3.14159 // 只读,不可重新赋值
var counter = 0 // 可变
counter++
// 给 val 重新赋值编译错误:
// pi = 3.0
// val 指向的对象内容仍可改:
val list = mutableListOf(1)
list.add(2) // 合法,引用未变
}
// 原则:能 val 就不 var
// val 是引用不可变,不是内容不可变

类型推断

编译器根据初始化表达式推断变量类型,声明可省略显式类型。推断结果与显式声明等价。

1
2
3
4
5
6
7
8
9
10
11
12
fun main() {
val count = 42 // Int
val name = "Kotlin" // String
val ratio = 3.14 // Double
val ok = true // Boolean
// 推断后类型固定:
// val x = 1 推断为 Int
// 无法推断时才需显式类型:
val empty: List<Int> = emptyList()
val map: Map<String, Int> =
mapOf("a" to 1)
}

显式类型

显式声明类型提升可读性,也是空安全与泛型的常见需求。类型写在变量名后,用冒号分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val name: String = "Rex"
var age: Int = 30
val height: Double = 1.75
val active: Boolean = true
// 空安全类型:
val maybe: String? = null
// 函数类型变量:
val handler: (Int) -> String =
{ it.toString() }
// 数组类型:
val nums: IntArray = intArrayOf(1, 2, 3)
}
// 显式类型不影响推断结果
// 团队成员协作时建议显式声明边界

编译期常量

const val 声明编译期常量,仅限基本类型与 String,只能出现在顶层或伴生对象中。

1
2
3
4
5
6
7
8
9
10
11
12
13
const val MAX_SIZE = 100
const val APP_NAME = "GuruToolkit"
const val RATE = 0.15
fun main() {
println(MAX_SIZE)
}
// 仅限基本类型与 String
// 必须顶层或伴生对象中声明
// 运行期计算用 val:
// val now = System.currentTimeMillis()
// const 在编译期内联替换
// 自定义类型不能用 const

顶层变量

文件顶层可直接声明变量与函数,无需类包裹。顶层 val/var 是文件级全局状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 文件级(无类包裹)
val appName = "demo"
var requestCount = 0
fun log(msg: String) {
println("[$appName] $msg")
}
// 顶层声明默认 public
// 同包其他文件可直接引用
// 内部实现加 private 防止暴露:
private val secret = "hidden"
// 顶层初始化在类加载时执行
// 多用 val,避免可变全局状态

解构声明

解构把对象拆分到多个变量。Pair、Triple 与 data class 都支持解构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val (name, age) = "Alice" to 30
println(name) // Alice
println(age) // 30
val (x, y) = Pair(1, 2)
val (a, b, c) = Triple(1, 2, 3)
}
// data class 自动支持解构:
data class Point(val px: Int, val py: Int)
fun show(p: Point) {
val (px, py) = p
println("$px,$py")
}
// 忽略部分用下划线:
// val (first, _) = listOf(1, 2)

类型别名

typealias 给已有类型起别名,提高可读性。不创建新类型,与原类型完全等价。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typealias UserId = Long
typealias Handler = (String) -> Unit
typealias NameMap = Map<String, String>
fun main() {
val id: UserId = 1001
val handle: Handler = { println(it) }
val names: NameMap = mapOf("a" to "b")
handle(names["a"] ?: id.toString())
}
// 泛型别名:
// typealias IntList = List<Int>
// 只是别名,不影响类型检查
// 函数类型别名让签名更可读

作用域与块

变量作用域由花括号块决定,内层可访问外层变量。同作用域不能重复声明同名变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val outer = 10
if (outer > 0) {
val inner = outer * 2 // 块内局部
println(inner)
}
// inner 在这里不可见
// 同作用域不能重复声明:
// val x = 1; val x = 2 错误
// 内层可遮蔽外层变量:
val name = "外层"
println(name)
}
// 遮蔽会造成困惑,尽量少用

3.数据类型

数字、字符、布尔、数组、区间、data class 与类型转换。

数字类型

Kotlin 提供 Byte/Short/Int/Long/Float/Double 六种数字类型,后缀 L、f、u 指定具体类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val b: Byte = 127
val s: Short = 32767
val i: Int = 2_147_483_647
val l: Long = 9_000_000_000L // 后缀 L
val f: Float = 3.14f // 后缀 f
val d: Double = 3.14159
// 下划线分隔增强可读性:
val million = 1_000_000
// 十六进制与二进制:
val hex = 0xFF
val bin = 0b1010
}
// 无符号类型(1.5+):val u: UInt = 42u

数值运算

算术、位运算与数学函数。Int 除 Int 得到 Int,注意整数除法截断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import kotlin.math.abs
import kotlin.math.max
fun main() {
val a = 7
val b = 2
println(a / b) // 3(整数除法截断)
println(a % b) // 1(取余)
println(a.toDouble() / b) // 3.5
println(abs(-5))
println(max(3, 9))
// 位运算:
println(1 shl 4) // 16
println(0b1100 and 0b1010)
}

字符与布尔

Char 表示单个字符(单引号),Boolean 只有 true/false。字符有完整的检查与转换方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val letter: Char = 'A'
val digit: Char = '9'
val flag: Boolean = true
// 字符方法:
println(letter.isLetter()) // true
println(digit.isDigit()) // true
println(letter.lowercaseChar())
println(letter.code) // 65 ASCII 码
// 布尔运算:
println(flag && true)
println(flag || false)
println(!flag)
}
// 转义字符:val tab = '\t'
// 布尔没有 0/1 语义,不能参与算术

数组

Array<T> 是引用类型数组,IntArray 等是基本类型数组(更高效)。数组大小固定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val nums = arrayOf(1, 2, 3) // Array<Int>
val ints = intArrayOf(1, 2, 3) // IntArray
val strs = arrayOf("a", "b")
println(nums[0])
nums[1] = 99
println(nums.size)
// 特定大小数组:
val zeros = IntArray(5) // 全 0
// 初始化 lambda:
val squares = IntArray(5) { it * it }
for (n in ints) println(n)
}
// 数组协变陷阱见 FAQ 节

区间

区间 a..b 表示闭区间,until 表示开区间。常配合 for 循环与 in 判断。

1
2
3
4
5
6
7
8
9
10
11
12
fun main() {
val r1 = 1..10 // 包含 10
val r2 = 1 until 10 // 不包含 10
val r3 = 10 downTo 1 // 倒序
val r4 = 1..10 step 2 // 步长 2
println(5 in r1) // true
println(11 in r1) // false
for (i in 1..3) println(i)
// 字符区间:
for (c in 'a'..'c') print(c)
}
// 区间主要用于 Int/Long/Char

字符串类型

String 是不可变字符序列,UTF-16 编码。字符串字面量用双引号,转义用反斜杠。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val s: String = "Hello"
val quote = "say \"hi\""
// 不可变:s[0] = 'h' 会报错
val greeting = "Hi, " + "Kotlin"
println(greeting)
// 遍历与索引:
for (c in "abc") println(c)
println("Kotlin"[1]) // 'o'
println("Kotlin".length) // 6
// 子串:
println("Kotlin".substring(1, 4)) // otl
}
// 字符串比较用 ==(结构相等)

data class

data class 自动生成 equals/hashCode/toString/copy 与解构,用于保存数据的纯值对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
data class Point(val x: Int, val y: Int)
fun main() {
val p = Point(1, 2)
val q = p.copy(y = 99) // 拷贝并修改
println(p) // Point(x=1, y=2)
println(p == Point(1, 2)) // true 结构相等
val (x, y) = p // 解构
println("$x, $y")
}
// data class 主构造需至少一个 val/var
// copy 是浅拷贝,嵌套对象仍共享
// 适合 DTO 与值对象

类型转换

Kotlin 不隐式转换数字类型,需显式 toXxx() 方法。小转大也可能溢出,需注意。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val i: Int = 42
val l: Long = i.toLong() // 显式转换
val d: Double = i.toDouble()
// String 转数字:
val n = "42".toInt()
val safe = "abc".toIntOrNull() // null
val s = 42.toString()
// 溢出截断:
val big = 1000.toByte() // -24
}
// 不会隐式转换:
// val x: Long = 42 编译错误
// 用 toXxxOrNull 系列避免抛异常

Unit 与 Nothing

Unit 表示无返回值(类似 Java void)。Nothing 表示永不返回,用于必定抛错或未实现的函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun log(msg: String): Unit {
println(msg)
}
// Unit 可省略声明:
fun log2(msg: String) {
println(msg)
}
// Nothing:永不返回
fun fail(msg: String): Nothing {
throw IllegalStateException(msg)
}
// 未实现占位:
fun todo(): Nothing =
throw NotImplementedError("未实现")
// Nothing 是所有类型的子类型
// 可用于任何期望类型的位置

4.引用与空安全

Kotlin 无裸指针:引用语义、可空类型、安全调用、Elvis、lateinit 与值类。

引用与对象

Kotlin 无裸指针,所有变量都是引用。JVM 自动管理生命周期,没有指针算术。

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
val a = "hello"
val b = a // 引用同一对象,非拷贝
println(a === b) // true 同一引用
// 没有 & 取地址,没有 * 解引用
// 基本类型也是对象(可空时装箱)
val n1 = 100
val n2 = 100
println(n1 == n2) // true 结构相等
}
// === 比较引用是否同一对象
// == 比较内容是否相等
// 无需手动释放内存,GC 自动回收

可空类型

类型加 ? 表示可为 null。可空类型不能直接调用方法,必须先判空,这是 Kotlin 空安全基石。

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
var name: String = "Kotlin" // 非空
var maybe: String? = null // 可空
// 不能直接调用:maybe.length 编译错误
// 判空后再用:
if (maybe != null) {
println(maybe.length)
}
// 判空后编译器智能转换,可安全访问
}
// 可空类型来自 Java 时可配合注解:
// @Nullable -> 可空,@NotNull -> 非空
// Java 无注解的类型是平台类型(String!)

安全调用

?. 在对象非空时调用方法,为空则整个表达式为 null。安全调用可链式书写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val maybe: String? = null
println(maybe?.length) // null
val sure: String? = "hi"
println(sure?.length) // 2
}
// 链式安全调用:
data class User(val name: String?)
fun show(u: User?) {
val len = u?.name?.length
println(len)
}
// 结合 let 判空处理:
// maybe?.let { println("非空: $it") }
// ?. 返回可空类型,需继续处理或给默认值

Elvis 运算符

?: 左侧为空时返回右侧默认值。右侧可为表达式,也可提前 return 或 throw。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val maybe: String? = null
val result = maybe ?: "default"
println(result) // default
// 结合 ?. 处理链:
val len = maybe?.length ?: 0
val upper = maybe?.uppercase() ?: ""
}
// 右侧可以是提前返回:
fun read(input: String?): String {
return input ?: return "空"
}
// 或抛异常:
// val value = maybe ?: throw IllegalArgumentException()
// Elvis 左侧必须可空,右侧非空

非空断言

!! 把可空类型强转为非空,为空时抛 NPE。应避免使用,仅在逻辑上确定非空时用。

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
val maybe: String? = "hello"
val len = maybe!!.length // 强制非空
println(len)
// 为 null 时抛 NullPointerException
}
// 更好的替代:
// 1. 安全调用 + Elvis:
// val l = maybe?.length ?: 0
// 2. 提前校验明确错误:
// val v = maybe ?: error("配置缺失")
// 3. require 前置校验
// 团队评审应拦截滥用 !!

lateinit 属性

lateinit 声明稍后初始化的非空 var,用于依赖注入或框架回调。使用前未初始化会抛异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
lateinit var config: String
fun setup() {
config = "initialized"
}
fun use() {
// 访问前检查是否已初始化:
if (::config.isInitialized) {
println(config)
}
}
// 限制:仅限 var,非空类型
// 不能是基本类型(Int 等)
// 不能用于局部变量
// 替代:可空类型 + 判空,或 by lazy

by lazy 委托

by lazy 首次访问时执行初始化,之后缓存结果。线程安全,适合昂贵的一次性初始化。

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main() {
val config: String by lazy {
println("首次访问才执行")
"计算出的配置"
}
println(config) // 打印初始化日志
println(config) // 使用缓存,不重复执行
}
// 指定线程模式:
// val v by lazy(LazyThreadSafetyMode.PUBLICATION)
// 默认 SYNCHRONIZED,多线程安全
// 只读,适合 val
// 依赖注入常用 lateinit 或 by lazy

拷贝与相等性

== 比较结构内容,=== 比较引用同一性。data class 的 copy() 做浅拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
data class Person(val name: String, val age: Int)
fun main() {
val a = Person("Alice", 30)
val b = Person("Alice", 30)
println(a == b) // true 内容相等
println(a === b) // false 不同对象
val c = a.copy(age = 31)
println(c) // Person(name=Alice, age=31)
}
// 嵌套对象浅拷贝共享:
data class Wrapper(val list: MutableList<Int>)
fun test() {
val w1 = Wrapper(mutableListOf(1))
val w2 = w1.copy()
w2.list.add(2)
println(w1.list) // [1, 2] 共享!
}
// 深拷贝需手动实现或序列化

value class

value class 包装单值并在运行时内联,消除装箱开销,类似类型安全的自定义包装类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@JvmInline
value class UserId(val id: Long)
fun process(userId: UserId) {
println(userId.id)
}
fun main() {
val id = UserId(1001)
process(id)
}
// 要求 @JvmInline 注解(Kotlin 1.5+)
// 仅一个属性
// 编译期内联,运行时无包装对象
// 适合包装类型:UserId、Money 等
// 不能用于 open 类、数组元素等场景

5.流程控制

if、when、for、while 与智能转换,表达式风格的流程控制。

if 表达式

if 是表达式,可返回值赋给变量。Kotlin 没有三元运算符,if-else 取代它。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val score = 85
val grade = if (score >= 90) {
"A"
} else if (score >= 80) {
"B"
} else {
"C"
}
println(grade) // B
// 单行写法:
val sign = if (score > 0) "正" else "负"
println(sign)
}
// 分支返回类型需一致或公共超类型

when 表达式

when 取代 switch,支持值、范围、类型与条件分支。表达式形式必须有 else 覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun main() {
val status = 404
val msg = when (status) {
200 -> "OK"
404 -> "未找到"
in 500..599 -> "服务器错误"
else -> "其他"
}
println(msg)
// 无参 when(相当于 if-else 链):
val n = 10
when {
n > 0 -> println("正数")
n < 0 -> println("负数")
else -> println("零")
}
// 多分支合并:
when (status) {
200, 201 -> println("成功")
else -> println("其他")
}
}

for 循环

for 遍历区间、集合、数组等可迭代对象。Kotlin 没有 C 风格的三表达式 for。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
// 遍历区间:
for (i in 1..5) println(i)
// 遍历集合:
val items = listOf("a", "b", "c")
for (item in items) println(item)
// 带索引:
for ((index, value) in items.withIndex()) {
println("$index -> $value")
}
// 遍历 map:
val map = mapOf("a" to 1, "b" to 2)
for ((k, v) in map) println("$k=$v")
}
// 倒序:for (i in 5 downTo 1)
// 步长:for (i in 0 until 10 step 2)

while 循环

while 先判断后执行,do-while 先执行后判断。条件必须是布尔表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun main() {
var n = 0
while (n < 3) {
println(n)
n++
}
// do-while 至少执行一次:
var m = 5
do {
println(m)
m++
} while (m < 3)
// 无限循环:
while (true) {
break
}
}
// 注意更新循环变量,避免死循环
// 集合遍历优先 for / forEach

break 与 continue

break 退出循环,continue 跳过本轮。标签 @ 可控制嵌套循环的跳转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
for (i in 1..5) {
if (i == 3) continue // 跳过 3
if (i == 5) break // 退出循环
println(i) // 1 2 4
}
// 标签跳出外层:
outer@ for (i in 1..3) {
for (j in 1..3) {
if (j == 2) continue@outer
println("$i,$j")
}
}
// 输出:1,1 2,1 3,1
}
// 标签也可用于 return:return@label

智能转换

类型检查后编译器自动转换类型,无需显式强转。判空、is 检查后即可安全使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
sealed class Shape
data class Circle(val r: Double) : Shape()
data class Rect(val w: Double, val h: Double) : Shape()
fun area(shape: Shape): Double = when (shape) {
is Circle -> Math.PI * shape.r * shape.r
is Rect -> shape.w * shape.h
}
fun main() {
val obj: Any = "hello"
if (obj is String) {
println(obj.length) // 自动转 String
}
println(area(Circle(1.0)))
}
// 前提:检查后变量未被修改
// 显式强转 as 仅在必要时使用

标签与跳转

标签 @ 标记循环或 lambda,配合 break、continue、return 实现精确跳转。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
// 循环标签:
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i * j > 4) break@outer
println("$i x $j")
}
}
// lambda 标签返回:
val list = listOf(1, 2, 3, 4)
list.forEach {
if (it == 3) return@forEach // 跳本轮
println(it) // 1 2
}
}
// 不带标签的 return 会退出外层函数
// 标签可任意命名:myLoop@

takeIf 与 takeUnless

takeIf 满足条件返回原对象否则 null,takeUnless 相反。常用于链式判空与过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
val n = 10
val positive = n.takeIf { it > 0 }
println(positive) // 10
val neg = n.takeUnless { it > 0 }
println(neg) // null
// 与 ?. 结合:
val text: String? = "hello"
val upper = text?.takeIf { it.isNotBlank() }
?.uppercase()
println(upper)
// 过滤后再判空:
val list = listOf(1, 2, 3)
val first = list.firstOrNull()
?.takeIf { it > 5 }
println(first) // null
}

6.函数与 Lambda

函数声明、lambda、高阶函数、扩展函数、作用域函数与中缀函数。

函数声明

fun 声明函数,参数带类型,返回类型在参数列表后。无返回值用 Unit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun add(a: Int, b: Int): Int {
return a + b
}
fun greet(name: String) {
println("Hello, $name")
}
// 无返回类型默认 Unit:
fun log(msg: String) { println(msg) }
fun main() {
val sum = add(1, 2)
greet("Kotlin")
log("sum = $sum")
}
// 参数是值传递(引用类型传引用)
// 函数默认 public

单表达式函数

函数体只有一个表达式时可用 = 简写,省略 return 与花括号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun square(x: Int) = x * x
fun isEven(n: Int) = n % 2 == 0
fun desc(n: Int) = if (n > 0) "正" else "非正"
// 等价写法:
fun square2(x: Int): Int {
return x * x
}
fun main() {
println(square(5)) // 25
println(isEven(4)) // true
println(desc(-1)) // 非正
}
// 适合简单纯函数
// 复杂逻辑用块体可读性更好

默认与命名参数

参数可设默认值,调用时可省略。命名参数可按任意顺序传入,提高可读性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun greet(name: String, prefix: String = "Hi") {
println("$prefix, $name!")
}
fun main() {
greet("Alice") // Hi, Alice!
greet("Bob", "Hello") // Hello, Bob!
// 命名参数(跳过默认):
greet(name = "Cathy", prefix = "你好")
// 顺序可调整:
greet(prefix = "Hola", name = "Dan")
}
// 默认值之后的参数:
// fun format(v: Int, base: Int = 10, pad: Int = 0)
// 布尔参数用命名参数更清晰

可变参数

vararg 接收任意数量参数,函数内作为数组使用。可用展开运算符 * 传入数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun sum(vararg nums: Int): Int {
return nums.sum()
}
fun main() {
println(sum(1, 2, 3)) // 6
println(sum(1, 2, 3, 4, 5)) // 15
// 展开数组传入:
val arr = intArrayOf(10, 20)
println(sum(*arr)) // 30
}
// vararg 参数类型是数组:
// vararg nums: Int -> IntArray
// 与普通参数混用:
// fun build(prefix: String, vararg parts: String)
// 通常放在参数列表末尾

Lambda 表达式

Lambda 是匿名函数,花括号包裹,-> 分隔参数与函数体。最后参数是 Lambda 时可用尾随语法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val sum = { a: Int, b: Int -> a + b }
println(sum(1, 2)) // 3
// 单参数省略声明,用 it:
val list = listOf(1, 2, 3)
println(list.map { it * 2 }) // [2, 4, 6]
// 尾随 Lambda 语法:
list.filter { it > 1 }
.forEach { println(it) }
// 显式类型:
val greet: (String) -> String =
{ "Hello, $it" }
println(greet("Kotlin"))
}
// Lambda 可捕获外层变量

高阶函数

高阶函数以函数为参数或返回值。map/filter 等标准库高阶函数是日常主力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun applyTwice(f: (Int) -> Int, x: Int): Int {
return f(f(x))
}
fun main() {
val inc = { n: Int -> n + 1 }
println(applyTwice(inc, 5)) // 7
// 常用高阶函数:
val nums = listOf(1, 2, 3, 4)
println(nums.map { it * 2 }) // 变换
println(nums.filter { it % 2 == 0 }) // 过滤
println(nums.fold(0) { acc, n -> acc + n }) // 累积
println(nums.any { it > 3 }) // 任一满足
println(nums.all { it > 0 }) // 全部满足
}
// 返回函数:
fun makeAdder(step: Int): (Int) -> Int = { it + step }

扩展函数

扩展函数为已有类添加方法,不修改原类。this 指向接收者对象,作用域仅限定义处。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun String.isPalindrome(): Boolean {
return this == this.reversed()
}
fun main() {
println("level".isPalindrome()) // true
println("hello".isPalindrome()) // false
// 泛型扩展:
println(listOf(1, 2).secondOrNull())
}
// 泛型扩展定义:
fun <T> List<T>.secondOrNull(): T? =
if (size >= 2) this[1] else null
// 调用时如普通方法
// 扩展不改变接收者类型

中缀函数

infix 关键字允许函数用空格调用,像操作符。要求成员或扩展函数且只有一个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
infix fun Int.times(str: String): String {
return str.repeat(this)
}
fun main() {
println(3 times "hi") // hihihi
// 标准库中缀:
val map = mapOf("a" to 1) // to 是中缀
println(2 in 1..3) // in 是中缀
}
// 自定义中缀:
// infix fun String.concat(b: String) = this + b
// println("foo" concat "bar") // foobar
// 要求:成员或扩展、单参数、无默认值

作用域函数

let/run/with/apply/also 在对象上下文中执行代码。let 返回结果,apply 返回接收者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
data class User(var name: String, var age: Int)
fun main() {
val user = User("Alice", 30)
// let:返回结果,it 引用对象
val greeting = user.let { "Hi ${it.name}" }
println(greeting)
// apply:返回接收者,常用于初始化
val u2 = User("Bob", 0).apply {
age = 25
}
println(u2)
// run:返回结果,this 引用对象
val info = user.run { "$name$age 岁" }
println(info)
// also:返回接收者,用于副作用
user.also { println(it.name) }
// with:非扩展,this 引用对象
val desc = with(user) { "$name/$age" }
println(desc)
}

局部函数

函数内可再定义局部函数,捕获外层变量。用于组织逻辑,减少重复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fun process(nums: List<Int>) {
fun isPositive(n: Int) = n > 0
fun show(label: String, v: Int) {
println("$label: $v")
}
val positives = nums.filter(::isPositive)
show("正数个数", positives.size)
}
// 局部函数可访问外层变量:
fun stats(nums: List<Int>) {
val total = nums.sum()
fun percent(n: Int) = n * 100.0 / total
println(percent(1))
}
fun main() {
process(listOf(-1, 2, -3, 4))
stats(listOf(1, 2))
}
// 仅在其外层函数内可见
// 适合封装递归辅助函数

7.字符串

字符串基础、模板、原始字符串、方法、拼接、格式化与转换。

字符串基础

String 是不可变字符序列。双引号字面量、转义序列、索引访问与遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val s = "Hello, Kotlin"
println(s.length) // 13
println(s[0]) // 'H'
println(s.first()) // 'H'
println(s.last()) // 'n'
println(s.isEmpty()) // false
println(s.isBlank()) // false
println(s.lowercase())
println(s.uppercase())
// 前缀后缀:
println(s.startsWith("Hello")) // true
println(s.endsWith("lin")) // true
println(s.contains("Kot")) // true
}
// 不可变:每次修改返回新字符串

字符串模板

$变量 与 ${表达式} 把值嵌入字符串,是最常用的字符串构建方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val name = "Kotlin"
val year = 2026
println("Hello, $name!") // Hello, Kotlin!
println("今年是 ${year - 2025} 周年")
// 对象属性:
val u = User("Alice")
println("用户: ${u.name}")
// 方法调用:
val list = listOf(1, 2)
println("数量: ${list.size}")
// 转义 $:
println("\$100")
}
data class User(val name: String)
// 复杂表达式必须用花括号

原始字符串

三引号 """ 原始字符串保留换行与格式,不做转义。trimIndent 去掉公共缩进。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
// 原始字符串:三个双引号包裹,不处理转义
val path = """C:\Users\name\docs"""
println(path) // 反斜杠原样保留
// 多行原始字符串保留换行与空格:
// val sql = """
// SELECT *
// FROM users
// """.trimIndent()
// trimIndent() 去掉每行公共缩进
// 模板仍可用:
val name = "Kotlin"
val msg = """Hello $name"""
println(msg)
}
// 不需要转义反斜杠与引号
// 适合多行文本、SQL、JSON
// 单行原始字符串也合法:"""abc"""

常用方法

标准库提供丰富的字符串方法:截取、替换、判空、去空白、填充。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val s = " Hello World "
println(s.trim()) // 去空白
println(s.substring(2, 7)) // "Hello"
println(s.replace("World", "Kt"))
println(s.reversed())
println(s.padStart(20, '*'))
println("".isEmpty()) // true
println(" ".isBlank()) // true
// 索引查找:
val t = "banana"
println(t.indexOf('a')) // 1
println(t.lastIndexOf('a')) // 5
println(t.indexOf("na")) // 2
println("ab".repeat(3))
}

拼接与 StringBuilder

循环拼接用 StringBuilder(或 buildString)避免创建大量中间字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun main() {
// 少量拼接用 + 或模板即可:
val a = "foo"
val b = "bar"
println("$a-$b")
// 循环拼接用 StringBuilder:
val sb = StringBuilder()
for (i in 1..5) {
sb.append("item$i; ")
}
println(sb)
// 函数式 buildString:
val result = buildString {
append("start")
for (i in 1..3) append(i)
}
println(result) // start123
// joinToString 拼集合:
println(listOf("a", "b").joinToString(", "))
}
// + 在循环里会反复创建新字符串

格式化

format 方法实现类似 printf 的格式化输出,支持宽度、精度与类型占位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val name = "Kotlin"
val score = 92.5
println("姓名: %s".format(name))
println("分数: %.1f".format(score)) // 92.5
println("补零: %05d".format(42)) // 00042
println("左对齐: %-8s|".format(name))
println("%x".format(255)) // ff
println("%b".format(true)) // true
// 指定宽度:
println("%10s".format(name))
}
// Java 的 String.format 等价:
// String.format("Hello %s", name)
// 格式字符串错误不报编译错,运行期抛异常

字符串转换

字符串与数字互转、转布尔。toXxxOrNull 系列安全转换避免异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
val s = "42"
println(s.toInt()) // 42
println(s.toLong())
println("3.14".toDouble())
// 安全转换,失败返回 null:
println("abc".toIntOrNull()) // null
println("42".toIntOrNull() ?: 0)
// 数字转字符串:
println(42.toString())
println(3.14.toString())
// 布尔转换:
println("true".toBooleanStrictOrNull())
// 转字符数组:
val chars = "hi".toCharArray()
println(chars.size)
}

分割与合并

split 按分隔符拆成列表,joinToString 把集合拼成字符串。支持多个分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val csv = "a,b,c"
val parts = csv.split(",")
println(parts) // [a, b, c]
// 多分隔符:
println("a-b_c".split('-', '_'))
// 集合转字符串:
val nums = listOf(1, 2, 3)
println(nums.joinToString(", ")) // 1, 2, 3
println(nums.joinToString(", ", "[", "]"))
// 限制分割数量:
println("a,b,c".split(",", limit = 2))
// 正则分隔符:
println("a, b; c".split(Regex("[,;\\s]+")))
}

8.集合

List、Set、Map、惰性序列、变换操作、分组与排序。

List 列表

listOf 创建只读列表,elementAt 与 get 按索引访问。默认 List 接口不可修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
val nums = listOf(1, 2, 3, 2)
println(nums[0]) // 1
println(nums.elementAt(1)) // 2
println(nums.size) // 4
println(nums.first()) // 1
println(nums.last()) // 2
println(nums.indexOf(2)) // 1
println(nums.contains(3)) // true
// 空列表:
val empty = emptyList<Int>()
println(empty)
// 构建指定元素:
println(List(3) { it * 10 }) // [0, 10, 20]
}
// 越界访问抛 IndexOutOfBounds
// firstOrNull 避免异常

Set 集合

setOf 创建无重复元素的集合,元素唯一。判断存在比 List 高效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val tags = setOf("a", "b", "a")
println(tags) // [a, b] 自动去重
println("a" in tags) // true
println(tags.contains("c")) // false
// 集合运算:
val s1 = setOf(1, 2, 3)
val s2 = setOf(2, 3, 4)
println(s1 intersect s2) // [2, 3]
println(s1 union s2) // [1, 2, 3, 4]
println(s1 subtract s2) // [1]
println(s1.isEmpty())
}
// 保留插入顺序(LinkedHashSet)
// 去重统计用 toSet

Map 字典

mapOf 创建键值对,to 或 Pair 构造条目。按键取值,未知键可返回默认。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val ages = mapOf("Alice" to 30, "Bob" to 25)
println(ages["Alice"]) // 30
println(ages["Cathy"]) // null
// 安全取值:
println(ages.getValue("Alice"))
println(ages.getOrDefault("Cathy", 0))
println(ages.getOrElse("Cathy") { -1 })
// 判断键存在:
println("Alice" in ages)
println(ages.containsKey("Bob"))
// 遍历:
for ((k, v) in ages) println("$k=$v")
println(ages.keys)
println(ages.values)
}

可变集合

mutableListOf/mutableSetOf/mutableMapOf 创建可修改集合。只读视图不能 add。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun main() {
val list = mutableListOf(1, 2)
list.add(3) // 尾部
list.add(0, 0) // 指定位置
list.removeAt(1)
list[0] = 99
println(list) // [99, 2, 3]
// 可变 Set:
val set = mutableSetOf("a")
set.add("b")
set.remove("a")
// 可变 Map:
val map = mutableMapOf("a" to 1)
map["b"] = 2
map.remove("a")
// 批量操作:
list.addAll(listOf(4, 5))
list.removeAll(listOf(2, 3))
println(list)
}
// 只读视图仍指向同一底层数据

惰性序列

Sequence 惰性求值,链式操作只在终止操作时逐元素执行。处理大数据避免中间集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val nums = (1..100).asSequence()
.filter { it % 2 == 0 }
.map { it * it }
.take(5)
.toList()
println(nums) // [4, 16, 36, 64, 100]
// 无限序列:
val naturals = generateSequence(1) { it + 1 }
naturals.take(3).forEach { println(it) }
}
// 与 List 区别:
// List 链每步生成新集合
// Sequence 单遍处理,节省内存
// 数据量大时用 Sequence
// 终止操作:toList、sum、count

变换操作

map/filter/flatMap 等是集合处理主力,函数式风格替代手写循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val nums = listOf(1, 2, 3, 4)
println(nums.map { it * 2 })
println(nums.filter { it % 2 == 0 })
println(nums.flatMap { listOf(it, it) })
// 聚合:
println(nums.sum())
println(nums.average())
println(nums.max())
println(nums.fold(0) { acc, n -> acc + n })
println(nums.reduce { acc, n -> acc + n })
// 分组:
println(nums.groupBy { it % 2 == 0 })
// 去重与排序:
println(nums.distinct())
println(nums.sorted())
println(nums.first { it > 2 })
}

分组与关联

groupBy 按条件分组,associate 构建键值关系,partition 一分两半。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun main() {
data class User(val name: String, val dept: String)
val users = listOf(
User("A", "dev"), User("B", "dev"),
User("C", "ops"),
)
// 分组:
val byDept = users.groupBy { it.dept }
println(byDept.keys) // [dev, ops]
// associate 构建 map:
val nameToDept = users.associate {
it.name to it.dept
}
println(nameToDept)
// 分区:
val nums = listOf(1, 2, 3, 4, 5)
val (even, odd) = nums.partition { it % 2 == 0 }
println(even) // [2, 4]
println(odd) // [1, 3, 5]
println(nums.count { it > 3 })
}

排序

sorted/sortedBy 返回新排序列表,sort 原地排序可变列表。支持自定义比较器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun main() {
val nums = listOf(3, 1, 2)
println(nums.sorted()) // [1, 2, 3]
println(nums.sortedDescending()) // [3, 2, 1]
data class User(val name: String, val age: Int)
val users = listOf(User("A", 30), User("B", 20))
println(users.sortedBy { it.age })
println(users.sortedByDescending { it.name })
// 多字段:
println(users.sortedWith(
compareBy<User> { it.age }.thenBy { it.name }
))
// 原地排序(可变集合):
val m = mutableListOf(3, 1, 2)
m.sort()
println(m)
}
// reversed() 反转

集合互转

集合、数组、Map 之间互相转换。toList/toSet/toMap/toTypedArray。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val list = listOf(1, 2, 2, 3)
println(list.toSet()) // [1, 2, 3]
println(list.toMutableList())
// 数组互转:
val arr = intArrayOf(1, 2, 3)
println(arr.toList())
println(listOf(1, 2).toIntArray().contentToString())
// List 转 Map:
val pairs = listOf("a" to 1, "b" to 2)
println(pairs.toMap())
// 字符串转集合:
println("a,b,c".split(","))
}
// toSet 自动去重
// 空集合类型推断:listOf<Int>()

9.内存与性能

JVM 垃圾回收、对象引用、装箱、分配优化与性能分析。

JVM 垃圾回收

Kotlin/JVM 由 GC 自动管理内存,无需手动释放。对象不可达时被回收。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Kotlin 运行在 JVM,内存自动管理
// 无需手动释放,对象不可达即被回收
fun create() {
val temp = StringBuilder() // 局部对象
// 函数返回后成为垃圾
}
fun main() {
// 大量临时对象触发 GC:
for (i in 1..100000) {
val s = "item$i"
}
// 减少分配可降低 GC 压力:
val sb = StringBuilder()
for (i in 1..100000) {
sb.clear()
sb.append("item$i")
}
println(sb.length)
}
// 弱引用:WeakReference 缓存不阻塞回收

对象引用

Kotlin 所有对象通过引用访问,无指针。引用类型决定共享与可达性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.ref.WeakReference
fun main() {
val list1 = mutableListOf(1, 2)
val list2 = list1 // 同一对象
list2.add(3)
println(list1) // [1, 2, 3] 共享!
}
// 弱引用:不阻塞 GC 回收
fun demo() {
val weak = WeakReference(mutableListOf(1))
println(weak.get()) // 可能为 null
}
// 软引用 SoftReference 内存不足才回收
// 循环引用可被 GC 处理(可达性分析)

inline 函数

inline 在调用处展开函数体,消除 Lambda 对象创建开销。reified 泛型需配合 inline。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun measure(block: () -> Unit) {
val start = System.nanoTime()
block()
println("耗时: ${System.nanoTime() - start}")
}
// inline 内联展开,零 Lambda 对象开销:
inline fun measureInline(block: () -> Unit) {
val start = System.nanoTime()
block()
}
fun main() {
measureInline { Thread.sleep(1) }
}
// reified 泛型需配合 inline:
inline fun <reified T> cast(obj: Any): T? =
obj as? T
// 过度内联增加字节码体积

分配优化

减少对象分配是 JVM 性能关键。复用对象、避免装箱、使用基本类型数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
// 避免装箱:
val ints = IntArray(100) // 基本类型
val boxed = Array(100) { 0 } // 装箱 Int
// 避免中间集合:
val result = (1..1000)
.asSequence()
.filter { it % 2 == 0 }
.toList()
println(ints.size + boxed.size + result.size)
// 复用可变集合:
val buffer = mutableListOf<Int>()
println(buffer)
}
// 字符串拼接用 buildString
// BAD:循环内 + 拼接反复创建字符串

基本类型装箱

非空基本类型局部变量用原始类型,可空或泛型时装箱。value class 可优化包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun main() {
var count = 0 // 原始 int
// 可空或集合元素装箱:
val maybe: Int? = null // Integer
val list = listOf(1, 2) // List<Integer>
// 数组:
val raw = intArrayOf(1) // int[]
val boxed = arrayOf(1) // Integer[]
println(list.size)
}
// 原始类型不可 == null
// 装箱 == 与 === 有差异
// 热路径避免装箱:
fun sum(raw: IntArray): Long {
var total = 0L
for (n in raw) total += n
return total
}
// value class 内联消除包装对象

性能调优

先测量再优化。常见优化点:数据结构、重复计算、惰性求值与算法复杂度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fun main() {
// 1. 选对集合:
val set = setOf("a", "b") // 查找 O(1)
val list = listOf("a", "b") // 查找 O(n)
println(set.contains("a"))
println(list.size)
// 2. 避免重复计算:
val size = list.size // 缓存到局部
println(size)
// 3. 惰性求值省中间集合:
val result = (1..1000)
.asSequence()
.filter { it % 2 == 0 }
.take(10)
.toList()
println(result)
}
// 先测量再优化,优先算法复杂度
// 别过早优化,先做基准测试

对象池

高并发场景复用对象避免频繁分配。池对象需线程安全与归还前状态清理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Connection
object Pool {
private val available = ArrayDeque<Connection>()
fun acquire(): Connection =
available.removeLastOrNull() ?: Connection()
fun release(c: Connection) {
if (available.size < 10) {
available.addLast(c)
}
}
}
fun main() {
val conn = Pool.acquire()
// 使用...
Pool.release(conn)
}
// 对象池适合昂贵对象:连接、线程、缓冲区
// 归还前清理状态
// 池满则丢弃,避免内存膨胀

性能分析

用 JFR、VisualVM 等工具分析 CPU 与内存。堆转储定位内存泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// JVM 常用分析工具:
// 1. JFR:java -XX:StartFlightRecording=...
// 2. VisualVM:图形界面看堆与 CPU
// 3. jcmd:jcmd <pid> GC.heap_dump
// 命令行快速查看:
// jps 列出 Java 进程
// jstat -gc <pid> 查看 GC 情况
// 内存泄漏定位:
// 1. 抓堆转储:jmap -dump:live,file=h.bin <pid>
// 2. 用 MAT / VisualVM 分析支配树
// 3. 检查静态集合是否无限增长
// 常见泄漏:静态缓存、未注销监听器
// 代码内测量:
fun main() {
val start = System.nanoTime()
// 业务代码...
println(System.nanoTime() - start)
}

10.面向对象

类、构造函数、属性、继承、接口、泛型、data class 与密封类。

类定义

class 声明类,属性可在主构造函数或类体中声明。类默认 final,不可继承。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person {
var name: String = ""
var age: Int = 0
fun introduce() {
println("我是 $name$age 岁")
}
}
fun main() {
val p = Person()
p.name = "Alice"
p.age = 30
p.introduce()
}
// 类默认 final,需继承时加 open
// 属性必须有初始值或初始化
// getter/setter 自动生成

构造函数

主构造函数在类头声明,参数可直接声明为属性。init 块在主构造后执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class User(
val name: String,
val age: Int = 0, // 默认值
) {
init {
println("初始化 $name")
}
// 次构造函数:
constructor(name: String, age: Int, email: String) :
this(name, age) {
println(email)
}
}
fun main() {
val u = User("Alice", 30)
val u2 = User("Bob")
val u3 = User("Cathy", 20, "[email protected]")
}
// 主构造参数加 val/var 变成属性
// 未加修饰仅是构造参数
// init 块按声明顺序执行

属性

属性自动生成 getter/setter。可自定义访问器,field 指幕后字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Circle {
var radius: Double = 1.0
// 自定义 getter:
val area: Double
get() = Math.PI * radius * radius
// 自定义 setter:
var diameter: Double
get() = radius * 2
set(value) {
radius = value / 2
}
}
fun main() {
val c = Circle()
c.radius = 10.0
println(c.area) // 314.159...
c.diameter = 20.0 // 走 setter
println(c.radius) // 10.0
}
// 私有 setter:var x set private set
// 属性是访问器,不是字段

继承与重写

open 类可被继承,open 成员可被重写。override 标记重写,super 访问父类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
open class Animal(val name: String) {
open fun speak() = "$name 叫了一声"
}
class Dog(name: String) : Animal(name) {
override fun speak() = "$name: 汪汪"
}
class Cat(name: String) : Animal(name) {
override fun speak() = super.speak() + ",喵"
}
fun main() {
val a: Animal = Dog("旺财")
println(a.speak()) // 多态
val cat = Cat("咪咪")
println(cat.speak())
}
// 类默认 final,需 open 才能继承
// override 成员默认 open
// 构造顺序:父类 init 先执行

接口

接口定义行为契约,可含默认实现与抽象成员。类可实现多个接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Drawable {
fun draw()
fun resize(factor: Double) { // 默认实现
println("resize x$factor")
}
}
interface Clickable {
fun click()
}
class Button : Drawable, Clickable {
override fun draw() = println("绘制按钮")
override fun click() = println("点击")
}
fun main() {
val btn = Button()
btn.draw()
btn.resize(2.0) // 默认实现
}
// 接口可声明属性:val name: String
// 接口无状态(不能保存字段值)
// 同签名方法需 override 消除歧义

抽象类

abstract class 定义部分实现,abstract 成员由子类实现。与接口不同,抽象类可保存状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
abstract class Shape {
abstract fun area(): Double
fun describe() = "面积: ${area()}"
}
class Square(val side: Double) : Shape() {
override fun area() = side * side
}
class Circle(val r: Double) : Shape() {
override fun area() = Math.PI * r * r
}
fun main() {
val shapes = listOf(Square(2.0), Circle(1.0))
shapes.forEach { println(it.describe()) }
}
// 抽象类可含属性与构造函数
// 接口更灵活,优先接口
// 抽象成员必须在子类实现

data class

data class 自动生成 equals/hashCode/toString/copy/componentN,适合值对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
data class Point(val x: Int, val y: Int)
fun main() {
val p1 = Point(1, 2)
val p2 = Point(1, 2)
println(p1 == p2) // true 结构相等
println(p1.hashCode() == p2.hashCode())
println(p1.toString()) // Point(x=1, y=2)
val p3 = p1.copy(y = 5)
println(p3) // Point(x=1, y=5)
val (x, y) = p1 // 解构
println("$x,$y")
}
// 主构造至少一个 val/var 参数
// copy 是浅拷贝
// 适合 DTO、结果对象、比较键

密封类

sealed class 限制子类在同一包/模块,when 分支可穷尽。表达受限的层次结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
sealed class Result {
data class Success(val data: String) : Result()
data class Failure(val error: Throwable) : Result()
}
fun handle(result: Result): String = when (result) {
is Result.Success -> "成功: ${result.data}"
is Result.Failure -> "失败: ${result.error}"
}
fun main() {
println(handle(Result.Success("ok")))
}
// 子类必须在同一包/模块
// when 穷尽时无需 else
// 新增子类时 when 编译器提醒
// 比枚举更灵活,可携带数据

单例对象

object 声明单例,线程安全地延迟初始化。整个程序只有一份实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object Logger {
private var count = 0
fun log(msg: String) {
count++
println("[$count] $msg")
}
}
fun main() {
Logger.log("启动")
Logger.log("结束")
}
// object 是单例:全局唯一实例
// 懒加载,首次访问时创建
// 线程安全
// 无构造函数
// 用途:工具类、配置、注册表

伴生对象

companion object 提供类级别成员,类似 Java 静态。可用 @JvmStatic 暴露给 Java。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Calculator {
companion object {
const val VERSION = "1.0"
fun create() = Calculator()
// 暴露为静态方法:
// @JvmStatic fun create() = Calculator()
}
}
fun main() {
println(Calculator.VERSION)
val calc = Calculator.create()
println(calc)
}
// 伴生对象是类的一部分
// 访问:类名.成员
// 伴生对象本身可命名:
// companion object Factory { }
// 一个类只能有一个伴生对象

泛型

泛型参数化类型。in/out 声明协变逆变,泛型函数与类型约束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Box<T>(val value: T)
fun <T> List<T>.firstOr(default: T): T =
if (isEmpty()) default else first()
fun main() {
val box = Box(42) // Box<Int>
val s = Box("hello") // Box<String>
println(listOf(1, 2).firstOr(0))
println(emptyList<Int>().firstOr(-1))
println(box.value + s.value.length)
}
// 类型约束:
fun <T : Comparable<T>> bigger(a: T, b: T): T =
if (a > b) a else b
// 协变 out / 逆变 in:
interface Producer<out T> { fun get(): T }
interface Consumer<in T> { fun put(item: T) }
// 星投影:List<*>

委托

by 关键字实现接口委托与委托属性。lazy/observable 管理属性,简化组合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import kotlin.properties.Delegates
interface Base {
fun print()
}
class BaseImpl : Base {
override fun print() = println("Base 实现")
}
class Derived(b: Base) : Base by b // 委托
fun main() {
val d = Derived(BaseImpl())
d.print() // 委托给 BaseImpl
}
// 委托属性:
var observable: String by Delegates.observable("init") {
_, old, new -> println("$old -> $new")
}
// 惰性委托:
val heavy: String by lazy { "expensive" }
// 委托给 map(动态属性):
class Person(map: Map<String, Any>) {
val name: String by map
}

11.错误处理

try 表达式、自定义异常、runCatching、Result、require/check 与资源管理。

try 表达式

try 是表达式,可返回 catch 中的值。catch 匹配异常类型,finally 必定执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun parse(text: String): Int = try {
text.toInt()
} catch (e: NumberFormatException) {
-1
}
fun risky() {
// 模拟可能失败的调用
}
fun main() {
println(parse("42")) // 42
println(parse("abc")) // -1
// finally 块:
try {
risky()
} catch (e: Exception) {
println("捕获: ${e.message}")
} finally {
println("始终执行")
}
}
// 多 catch 按异常类型匹配

抛出异常

throw 抛出异常对象。自定义消息与异常链。require/check 是参数校验惯用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fun validate(age: Int) {
if (age < 0) {
throw IllegalArgumentException("年龄不能为负")
}
}
fun main() {
try {
validate(-1)
} catch (e: IllegalArgumentException) {
println(e.message)
}
}
// 抛自定义消息:
// throw IllegalStateException("状态非法")
// 带原因链:
// throw RuntimeException("上层错误", Throwable("底层"))
// 参数校验惯用法:
// require(age >= 0) { "年龄必须非负" }
// requireNotNull(text)
// check(state == "ready") { "未就绪" }

自定义异常

继承 Exception 或 RuntimeException 自定义异常。可携带额外字段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class ValidationError(
val field: String,
message: String,
) : Exception("字段 $field 无效: $message")
fun parseUser(input: String) {
if (input.isBlank()) {
throw ValidationError("input", "不能为空")
}
}
fun main() {
try {
parseUser("")
} catch (e: ValidationError) {
println("字段: ${e.field}")
println("消息: ${e.message}")
}
}
// 继承 Exception 或 RuntimeException
// Kotlin 无检查异常限制
// 可给异常加 data class 方便相等比较

runCatching

runCatching 捕获异常并返回 Result。函数式错误处理替代 try-catch。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fun risky() = 42
fun main() {
val result = runCatching { "42".toInt() }
println(result.getOrNull()) // 42
val failed = runCatching { "x".toInt() }
println(failed.getOrNull()) // null
// 默认值:
val n = runCatching { "x".toInt() }
.getOrDefault(0)
println(n)
// 分支处理:
runCatching { risky() }
.onSuccess { println("成功: $it") }
.onFailure { println("失败: ${it.message}") }
// 转换:
val len = runCatching { "hi" }
.map { it.length }
.getOrDefault(-1)
println(len)
}
// 注意 runCatching 也会捕获 CancellationException

Result 类型

Result<T> 封装成功值或失败异常,支持 map/onSuccess 等操作。常用作函数返回类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun divide(a: Int, b: Int): Result<Int> =
runCatching { a / b }
fun main() {
val ok = divide(10, 2)
val bad = divide(10, 0)
println(ok.getOrNull()) // 5
println(bad.exceptionOrNull()?.message)
// 链式操作:
divide(10, 2)
.map { it * 2 }
.onSuccess { println("结果 $it") }
.onFailure { println("出错") }
// 解包(失败抛异常):
val v = divide(10, 2).getOrThrow()
println(v)
}
// Result 用于单值封装
// 集合用 fold 聚合多个 Result

资源管理

use 扩展自动关闭 Closeable/AutoCloseable 资源,类似 Java try-with-resources。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.File
fun readFirstLine(path: String): String? =
File(path).bufferedReader().use { reader ->
reader.readLine()
}
fun main() {
println(readFirstLine("data.txt"))
}
// use 确保 finally 自动关闭:
// File("f").bufferedReader().use { it.readText() }
// useLines 流式逐行:
fun first10(path: String) {
File(path).useLines { lines ->
lines.take(10).forEach { println(it) }
}
}
// 自定义资源:
class Resource : AutoCloseable {
override fun close() = println("关闭")
}
fun demo() {
Resource().use { println("使用中") }
}

require 与 check

require 参数校验,check 状态校验,error 主动失败。失败抛异常并打印消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun setAge(age: Int) {
require(age in 0..150) {
"年龄超出范围: $age"
}
}
fun main() {
// require 失败抛 IllegalArgumentException
setAge(30)
// check 失败抛 IllegalStateException:
check(true) { "状态正常" }
// error() 主动失败:
val value: String? = "ok"
val v = value ?: error("值为空")
println(v)
}
// assert 断言仅调试启用:
// assert(false) { "仅测试" }
// 启用断言需 -ea 参数

Nothing 与失败

Nothing 表示永不返回,用于 todo/error 等未实现或必失败路径。类型系统理解控制流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
fun fail(message: String): Nothing {
throw IllegalStateException(message)
}
fun main() {
val x = "a"
val result = when (x) {
"a" -> 1
else -> fail("未知输入")
}
println(result) // 1
}
// Nothing 是任意类型的子类型:
fun nullable(): String? = fail("空的")
// 未实现占位:
fun notImplemented(): Nothing {
throw NotImplementedError("尚未实现")
}
// error() 返回 Nothing:
// val cfg = mapOf("port" to 8080)
// val port = cfg["port"] ?: error("缺少端口")

12.输入输出

文件读写、Path API、标准输入输出、JSON 序列化与文件操作。

读取文件

readText 一次读整个文件为字符串,readBytes 读字节。指定字符集避免乱码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.File
fun main() {
val text = File("data.txt").readText()
println(text)
// 指定字符集:
val utf8 = File("data.txt")
.readText(Charsets.UTF_8)
println(utf8.length)
// 读字节:
val bytes = File("image.png").readBytes()
println(bytes.size)
}
// 相对路径基于工作目录
// 文件不存在抛 FileNotFoundException
// 读取失败用 runCatching 包裹处理

写入文件

writeText 覆盖写入,appendText 追加,writeBytes 写字节。目录需先存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.File
fun main() {
File("out.txt").writeText("Hello, Kotlin!")
// 追加:
File("log.txt").appendText("新行\n")
// 写字节:
File("data.bin").writeBytes(byteArrayOf(1, 2))
// 指定字符集:
File("out.txt").writeText("中文内容", Charsets.UTF_8)
// 多行:
File("out.txt").writeText(
listOf("a", "b", "c").joinToString("\n")
)
}
// writeText 默认覆盖原内容
// 目录不存在会抛 IOException
// 大文件用缓冲流逐块写

逐行读取

readLines 读全部行,useLines 惰性流式逐行处理大文件。forEachLine 便捷遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.File
fun main() {
// 全部行(小文件):
val lines = File("data.txt").readLines()
lines.forEach { println(it) }
// 惰性逐行(大文件):
File("big.log").useLines { seq ->
seq.filter { "ERROR" in it }
.take(5)
.forEach { println(it) }
}
}
// useLines 自动关闭文件
// 大文件不要用 readLines 一次读入
// forEachLine 便捷逐行遍历

Path API

java.nio.file.Path 处理路径,Files 提供读写与目录操作。路径拼接跨平台。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.nio.file.Files
import java.nio.file.Path
fun main() {
val base = Path.of("data")
val file = base.resolve("note.txt")
println(file) // data\note.txt
println(Files.exists(file))
println(file.fileName)
println(file.parent)
// 创建目录:
Files.createDirectories(Path.of("a/b/c"))
// 复制与移动:
Files.copy(file, Path.of("backup.txt"))
Files.move(file, Path.of("new.txt"))
}
// Path.of 路径分隔符自动适配平台
// Files.list 需 use 关闭流
// Files.readString / writeString 更简单

标准输入

readln 读取一行,readlnOrNull 读取可空。读后手动转换为所需类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
println("输入名字:")
val name = readln() // 读一行
println("你好, $name")
// 读取可能为空:
val line = readlnOrNull() ?: ""
// 读数字(手动转换):
val age = readln().toIntOrNull() ?: 0
println("年龄: $age")
}
// 批量行输入:
// generateSequence { readlnOrNull() }
// .take(5).forEach { println(it) }
// readln 在 EOF 时抛异常,用 readlnOrNull 更稳

标准输出

println/print 输出到标准输出,println 自动换行。可输出到错误流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
println("hello") // 带换行
print("world") // 不带换行
print("!\n")
val name = "Kotlin"
println("Hello, $name")
// 格式化:
println("%5.2f".format(3.14159)) // 3.14
// 输出到错误流:
System.err.println("错误信息")
}
// println 每次加换行符
// 大量输出用 BufferedWriter 缓冲
// PrintWriter 更贴近 Java 写法

JSON 序列化

kotlinx.serialization 官方序列化库,@Serializable 注解生成编解码器。Jackson 可作替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 依赖:
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
@Serializable
data class User(val name: String, val age: Int)
fun main() {
val json = Json { prettyPrint = true }
val u = User("Alice", 30)
val text = json.encodeToString(u)
println(text)
val back = json.decodeFromString<User>(text)
println(back)
}
// 需在 Gradle 启用 kotlin serialization 插件
// 忽略未知字段:Json { ignoreUnknownKeys = true }

文件操作

判断存在、删除、重命名、大小与权限。walkTopDown 递归遍历目录。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.File
fun main() {
val f = File("data.txt")
println(f.exists())
println(f.isFile)
println(f.isDirectory)
println(f.length()) // 字节大小
println(f.lastModified())
// 删除与重命名:
f.delete()
f.renameTo(File("new.txt"))
// 创建:
f.createNewFile()
// 临时文件:
val tmp = File.createTempFile("prefix", ".tmp")
tmp.deleteOnExit()
// 递归遍历:
File(".").walkTopDown()
.filter { it.extension == "kt" }
.forEach { println(it) }
}

13.常见误区

Kotlin 日常开发最容易踩的坑与正确写法,BAD/GOOD 成对展示。

滥用 !!

!! 在空值时抛 NPE,应避免。用安全调用、Elvis 或提前校验替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun main() {
val maybe: String? = null
// BAD:空值直接崩溃
// val len = maybe!!.length
// GOOD:Elvis 提供默认值
val len = maybe?.length ?: 0
// GOOD:提前校验明确错误
val text = maybe ?: error("配置缺失")
// GOOD:安全调用再处理
maybe?.let { println(it.length) }
println(len)
println(text)
}
// !! 只在逻辑上保证非空时使用
// 团队评审应拦截滥用 !!

智能转换失效

可变属性或委托属性智能转换失败。赋值后需重新检查或使用局部变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Box(var value: Any? = null)
// BAD:var 属性智能转换失败
fun bad(b: Box) {
// println(b.value.length) 编译错误
}
// GOOD:先取到局部 val 再判断
fun good(b: Box) {
val v = b.value
if (v is String) {
println(v.length)
}
}
fun main() {
good(Box("hello"))
}
// 原因:var 可能被并发修改
// 委托属性同样无法智能转换
// 智能转换前提:检查后不再变化

== 与 ===

== 结构相等比较内容,=== 引用相等比较地址。对象与基本类型要分清。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
fun main() {
val s1 = "abc"
val s2 = "abc"
// BAD:用 === 比较内容,结果依赖常量池
println(s1 === s2) // 可能 true 可能 false
// GOOD:结构比较用 ==
println(s1 == s2) // true
}
// BAD:普通类不重写 equals
class Point(val x: Int)
fun badEq() {
println(Point(1) == Point(1)) // false
}
// GOOD:data class 自动 equals
fun goodEq() {
println(Point2(1) == Point2(1)) // true
}
data class Point2(val x: Int)
// 数值装箱比较:
// val a: Int? = 100
// val b: Int? = 100
// a == b // true,结构相等

可空拼接

可空值与字符串拼接时模板对 null 显示 "null"。需显式处理空值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val name: String? = null
// BAD:输出字符串 "null"
println("欢迎 $name")
// GOOD:Elvis 给默认值
println("欢迎 ${name ?: "访客"}")
// GOOD:?. 安全转换再兜底
println("欢迎 ${name?.uppercase() ?: ""}")
}
// BAD:拼接直接变成 null.toString
// val msg = "用户:" + name // 用户:null
// GOOD:
// val msg = "用户:${name ?: "未知"}"
// 模板里属性访问记得加花括号

模板可空陷阱

模板 $a.b 解析为 a.b 属性。可空对象属性访问需 ${a?.b}。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
data class User(val nick: String?)
fun main() {
val u = User(null)
// BAD:$u.nick 被解析为属性访问
// println("$u.nick") 语法歧义
// GOOD:表达式加花括号
println("${u.nick}") // null
// BAD:直接显示 null 难读
println("昵称: ${u.nick}")
// GOOD:Elvis 兜底
println("昵称: ${u.nick ?: "未设置"}")
}
// 单变量模板:$name 正确
// 带点访问必须加花括号:${u.nick}

数组协变陷阱

Array<String> 可赋给 Array<Any>,运行期可能抛 ArrayStoreException。用 List 更安全。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
// BAD:数组协变导致运行期崩溃
val strings = arrayOf("a", "b")
val anys: Array<Any> = strings
// anys[0] = 1 抛 ArrayStoreException
// GOOD:用 List(协变安全)
val list: List<Any> = listOf("a", "b")
println(list.size)
}
// GOOD:新建数组而非强转
// val safe = strings.map { it as Any }.toTypedArray()
// 原因:JVM 数组运行期保留元素类型
// Kotlin 保留数组协变以兼容 Java
// 只读 List 不可变,无此问题

lateinit 未初始化

使用前未初始化抛 UninitializedPropertyAccessException。访问前检查 isInitialized。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Service {
lateinit var client: String
fun start() {
client = "connected"
}
fun ping() {
// BAD:未调用 start 则崩溃
// println(client)
// GOOD:先检查再访问
if (::client.isInitialized) {
println(client)
}
}
}
fun main() {
val s = Service()
s.start()
s.ping()
}
// BAD:初始化为空串"伪装"掩盖问题
// lateinit var s: String = ""
// GOOD:val + by lazy 更安全
// 或可空类型 + 判空

作用域函数混淆

let/apply/run 返回值不同。用错导致返回对象而非结果。按用途选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data class User(var name: String)
fun main() {
val u = User("Alice")
// BAD:apply 想拿结果却返回对象
val upper = u.apply {
name = name.uppercase()
}
println(upper.name) // ALICE,但 upper 是 User
// GOOD:let 返回 Lambda 结果
val upper2 = u.let { it.name.uppercase() }
println(upper2) // ALICE,String
}
// 选择规则:
// 返回接收者 -> apply / also
// 返回结果 -> let / run / with
// 需判空 -> let + ?.

伴生对象混淆

伴生对象成员不是类静态字段。Kotlin 用伴生对象实现静态语义,需 @JvmStatic 暴露。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Config {
companion object {
val version = "1.0"
fun load() = println("加载")
}
}
fun main() {
// GOOD:Kotlin 内直接用类名访问
println(Config.version)
Config.load()
}
// BAD:以为伴生对象成员是 static 字段
// Java 反射取不到静态字段
// GOOD:@JvmStatic 暴露给 Java
class Config2 {
companion object {
@JvmStatic
fun load() = println("加载")
}
}
// 伴生对象仍是对象实例
// Java 访问:Config.Companion.load()

只读视图误解

只读 List 接口只是视图,底层可变集合仍可修改。共享可变状态需拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fun main() {
// BAD:以为只读视图就是不可变
val mutable = mutableListOf(1, 2)
val view: List<Int> = mutable
mutable.add(3) // view 也变了!
println(view) // [1, 2, 3]
// GOOD:不可变拷贝
val frozen = mutable.toList()
println(frozen)
// GOOD:真正不可变用 listOf
val fixed = listOf(1, 2)
println(fixed)
}
// 多线程共享集合:
// BAD:共享可变集合
// GOOD:不可变集合或加锁
// toList/toSet 生成新不可变副本

14.并发与协程

线程、协程、async/await、Channel、Flow、互斥锁与超时控制。

线程基础

Thread 与 thread{} 创建线程。join 等待结束。并发共享状态需同步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import kotlin.concurrent.thread
fun main() {
// 创建并启动线程:
val t = thread(start = true) {
repeat(3) {
println("子线程: $it")
Thread.sleep(100)
}
}
t.join() // 等待子线程结束
println("主线程结束")
}
// 用 ExecutorService 管理线程池:
// val pool = Executors.newFixedThreadPool(4)
// 多线程共享可变状态需同步
// Thread.sleep 是阻塞等待

协程入门

launch 启动轻量协程,delay 挂起不占线程。runBlocking 阻塞等待协程完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import kotlinx.coroutines.*
fun main() = runBlocking {
// launch 启动协程(非阻塞):
launch {
delay(100) // 挂起,不占线程
println("协程 A")
}
launch { println("协程 B") }
// runBlocking 会等待所有协程完成
println("顺序执行到这里")
}
// suspend 函数只能在协程内调用
// delay 挂起协程,Thread.sleep 阻塞线程
// 依赖:kotlinx-coroutines-core

async 与 await

async 并发执行并返回 Deferred,await 获取结果。awaitAll 等待多个任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import kotlinx.coroutines.*
suspend fun fetch(id: Int): String {
delay(100)
return "数据$id"
}
fun main() = runBlocking {
// async 并发执行,await 取结果:
val a = async { fetch(1) }
val b = async { fetch(2) }
println(a.await() + b.await()) // 数据1数据2
// 并发列表:
val results = (1..3).map { id ->
async { fetch(id) }
}.awaitAll()
println(results)
}
// Deferred<T> 类似 Future
// async 出错时 await 抛异常
// 大量任务用 map + awaitAll 并发

withContext

withContext 切换调度器执行阻塞任务。IO 任务用 Dispatchers.IO,计算用 Default。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import kotlinx.coroutines.*
fun main() = runBlocking {
// IO 密集任务切到 IO 调度器:
val data = withContext(Dispatchers.IO) {
Thread.sleep(10) // 模拟阻塞 IO
"从磁盘读取"
}
println(data)
// Default:CPU 密集计算
val sum = withContext(Dispatchers.Default) {
(1..1000).sum()
}
println(sum)
}
// Dispatchers.IO:网络/磁盘 IO 任务
// Dispatchers.Default:计算密集任务
// Dispatchers.Main:Android 主线程
// withContext 返回最后表达式结果

Channel 通道

Channel 在协程间传输数据,send/receive 是挂起函数。支持缓冲与关闭。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
fun main() = runBlocking {
val channel = Channel<Int>(capacity = 5)
// 生产者:
launch {
for (i in 1..3) {
channel.send(i)
println("发送 $i")
}
channel.close()
}
// 消费者:
for (v in channel) {
println("接收 $v")
}
}
// 无缓冲:发送与接收同步配对
// 有缓冲:capacity 指定缓冲大小
// 迭代 channel 在关闭后结束
// 多生产多消费用 broadcast

Flow 数据流

Flow 是异步冷流,collect 时才执行。支持 map/filter 等操作符与背压。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun countDown(): Flow<Int> = flow {
for (i in 3 downTo 1) {
emit(i) // 发射元素
delay(100)
}
}
fun main() = runBlocking {
countDown()
.map { "第$it 秒" }
.onEach { println(it) }
.collect()
// flowOf / asFlow 便捷创建:
listOf(1, 2).asFlow().collect { println(it) }
}
// Flow 是冷流:collect 时才执行
// 支持背压:按消费速度发射
// collect 是挂起函数

互斥与同步

原子类 AtomicInteger 做无锁计数。协程内用 Mutex 保护临界区。线程内用 synchronized。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import java.util.concurrent.atomic.AtomicInteger
// 共享计数器:
val counter = AtomicInteger(0)
fun main() = runBlocking {
// 方式一:原子类
val jobs = List(10) {
launch { repeat(1000) { counter.incrementAndGet() } }
}
jobs.forEach { it.join() }
println(counter.get()) // 10000
// 方式二:协程互斥锁
val mutex = Mutex()
var total = 0
launch {
repeat(1000) {
mutex.withLock { total++ } // 临界区
}
}.join()
println(total)
}
// @Synchronized 同步普通线程方法
// volatile 保证可见性,不保证原子性
// 协程内用 Mutex,线程用 synchronized

超时与取消

withTimeout 限制协程执行时间。job.cancel 协作式取消,需检查 isActive。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import kotlinx.coroutines.*
fun main() = runBlocking {
// 超时控制:
try {
withTimeout(500) {
repeat(10) {
delay(200) // 总耗时 2 秒
}
}
} catch (e: TimeoutCancellationException) {
println("超时了")
}
// 取消协程:
val job = launch {
while (isActive) {
println("工作中...")
delay(100)
}
}
delay(300)
job.cancel() // 请求取消
println("已取消")
}
// 协作式取消:需检查 isActive
// 挂起点会响应取消,抛 CancellationException
// 清理资源用 finally + withContext(NonCancellable)

15.网络编程

HTTP 请求、Ktor Client、JSON 解析、TCP Socket、HTTP 服务器与 WebSocket。

URL 简单请求

URL.readText 快速 GET。openConnection 可配置超时。适合轻量小响应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.net.URL
fun main() {
// 简单 GET 请求:
val html = URL("https://example.com")
.readText()
println(html.take(100))
// 带超时:
val conn = URL("https://example.com")
.openConnection()
conn.connectTimeout = 3000
conn.readTimeout = 3000
val body = conn.getInputStream().readBytes()
println(body.size)
}
// readText 适合小响应
// 生产环境用 HttpClient 更可控
// 注意处理网络异常

JDK HttpClient

JDK 11+ 内置 HttpClient 发送 HTTP 请求。支持异步 sendAsync 与响应体转换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
fun main() {
val client = HttpClient.newBuilder()
.connectTimeout(java.time.Duration.ofSeconds(5))
.build()
val request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.GET()
.build()
val response = client.send(
request, HttpResponse.BodyHandlers.ofString()
)
println(response.statusCode())
println(response.body().take(100))
}
// JDK 11+ 内置 HTTP 客户端
// 异步版用 sendAsync 返回 CompletableFuture
// 支持 GET/POST/Header/超时配置

Ktor Client

Ktor Client 是跨平台 HTTP 库,支持协程与多引擎。适合网络密集任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 依赖:io.ktor:ktor-client-core
// io.ktor:ktor-client-java
import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.runBlocking
suspend fun fetch() {
val client = HttpClient()
val body = client.get("https://example.com")
.bodyAsText()
println(body.take(100))
client.close()
}
fun main() = runBlocking {
fetch()
}
// Ktor Client 跨平台 HTTP 库
// 支持 WebSocket、SSE、认证
// 引擎可换:OkHttp / CIO / Java

解析响应 JSON

HttpClient 获取响应后,用 kotlinx.serialization 把 JSON 反序列化为 data class。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.decodeFromString
@Serializable
data class Repo(val name: String, val url: String)
fun main() {
val client = HttpClient.newBuilder().build()
val req = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/repos/kotlin/kotlinx.serialization"))
.header("Accept", "application/json")
.GET()
.build()
val body = client.send(
req, HttpResponse.BodyHandlers.ofString()
).body()
// 解析 JSON:
val repo = Json.decodeFromString<Repo>(body)
println(repo.name)
}
// 先 @Serializable 再 decodeFromString
// 字段名不一致用 @SerialName
// 忽略未知字段:Json { ignoreUnknownKeys = true }

TCP Socket

ServerSocket 监听端口,accept 接受连接。Socket 读写字节流。注意关闭资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.net.ServerSocket
import java.net.Socket
fun main() {
// 服务端监听端口:
val server = ServerSocket(9999)
println("监听 9999...")
val client = server.accept() // 阻塞等连接
val input = client.getInputStream().bufferedReader()
println("收到: " + input.readLine())
client.close()
server.close()
}
// 客户端连接:
// val s = Socket("localhost", 9999)
// s.getOutputStream().write("hi".toByteArray())
// 生产用 NIO / Netty 处理高并发
// 记得关闭资源(use 自动关闭)

HTTP 服务器

JDK 内置 HttpServer 创建轻量 HTTP 服务。createContext 注册路由处理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.sun.net.httpserver.HttpServer
import java.net.InetSocketAddress
fun main() {
val server = HttpServer.create(
InetSocketAddress(8080), 0
)
server.createContext("/hello") { exchange ->
val body = "Hello, Kotlin!".toByteArray()
exchange.responseHeaders.add(
"Content-Type", "text/plain"
)
exchange.sendResponseHeaders(200, body.size.toLong())
exchange.responseBody.use { it.write(body) }
}
server.start()
println("服务已启动: http://localhost:8080/hello")
}
// JDK 内置轻量 HTTP 服务器
// 适合本地工具与简单接口
// 生产推荐 Ktor / Spring Boot
// 停止:server.stop(0)

REST 设计要点

REST 用资源与 HTTP 方法表达操作。data class 描述请求响应,状态码语义准确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class Todo(
val id: Int,
val title: String,
val done: Boolean = false,
)
// REST 设计要点:
// GET /todos 列表
// GET /todos/{id} 详情
// POST /todos 创建(返回 201)
// PUT /todos/{id} 整体更新
// DELETE /todos/{id} 删除(返回 204)
fun main() {
val todos = listOf(Todo(1, "学习 Kotlin"))
println(Json.encodeToString(todos))
}
// 状态码语义要准确
// 错误返回统一错误结构
// 分页参数:page, size

WebSocket

OkHttp 提供 WebSocket 客户端,onOpen/onMessage 回调处理双向消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 依赖:com.squareup.okhttp3:okhttp
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
fun main() {
val client = OkHttpClient()
val request = Request.Builder()
.url("wss://echo.websocket.org")
.build()
val ws = client.newWebSocket(request,
object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) {
webSocket.send("hello")
}
override fun onMessage(webSocket: WebSocket, text: String) {
println("收到: $text")
}
override fun onClosing(ws: WebSocket, code: Int, reason: String) {
ws.close(code, reason)
}
})
Thread.sleep(2000)
ws.close(1000, "再见")
client.dispatcher.executorService.shutdown()
}
// WebSocket 全双工通信
// 断线重连需自己实现
// onMessage 也接收 ByteString 二进制

16.日期与时间

Instant、LocalDate、格式化、解析、Duration、Period 与带时区时刻。

当前时间戳

Instant.now 记录瞬时点。currentTimeMillis 取毫秒,nanoTime 测量间隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.time.Instant
fun main() {
// 当前时刻:
val now = Instant.now()
println(now)
// 纪元秒/毫秒:
println(Instant.now().epochSecond)
println(Instant.now().toEpochMilli())
// 旧式毫秒时间戳:
println(System.currentTimeMillis())
println(System.nanoTime())
}
// Instant 是瞬时点,适合记录时间戳
// nanoTime 用于测量间隔,非时钟时间
// UTC 时刻与本地时间区分开

LocalDate

LocalDate 表示年月日,of/now 创建。加减天数、判断与取分量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.time.LocalDate
fun main() {
val today = LocalDate.now()
println(today)
val date = LocalDate.of(2026, 8, 2)
println(date)
// 计算:
println(date.plusDays(1))
println(date.minusMonths(2))
println(date.plusWeeks(1))
// 判断:
println(today.isAfter(date))
println(today.dayOfWeek)
println(today.dayOfMonth)
println(today.month)
println(today.isLeapYear)
}
// LocalDate 只有日期,无时间无时区
// 不可变,线程安全

LocalDateTime

LocalDateTime 含日期与时间。withHour 调整字段,plusHours 加减。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.time.LocalDateTime
fun main() {
val now = LocalDateTime.now()
println(now)
val dt = LocalDateTime.of(2026, 8, 2, 15, 30)
println(dt.hour)
println(dt.minute)
println(dt.second)
// 调整:
println(dt.withHour(9))
println(dt.plusHours(5))
// 与 LocalDate 互转:
println(dt.toLocalDate())
println(dt.toLocalTime())
}
// LocalDateTime 日期+时间,无时区
// 字段操作返回新实例(不可变)

格式化输出

DateTimeFormatter.ofPattern 自定义格式。yyyy-MM-dd HH:mm:ss 是常用模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
fun main() {
val now = LocalDateTime.now()
// 常用模式:
println(now.format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
))
println(now.format(
DateTimeFormatter.ofPattern("yyyy/MM/dd")
))
println(now.format(
DateTimeFormatter.ofPattern("HH:mm")
))
// 内置格式器:
println(now.format(DateTimeFormatter.ISO_LOCAL_DATE))
}
// 模式字母表:y年 M月 d日 H时 m分 s秒
// 格式器线程安全,可复用为 val
// 大小写敏感:M 月,m 分

解析日期

LocalDate.parse 解析字符串。自定义格式需配 DateTimeFormatter。失败抛异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.format.DateTimeParseException
fun main() {
// 解析 ISO 格式:
println(LocalDate.parse("2026-08-02"))
// 自定义格式:
val fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd")
println(LocalDate.parse("2026/08/02", fmt))
// 安全解析:
try {
LocalDate.parse("2026-13-40")
} catch (e: DateTimeParseException) {
println("日期格式错误: ${e.message}")
}
}
// 解析失败抛 DateTimeParseException
// 批量解析先校验再转换
// 输入不可信时用 try 包裹

Duration 时长

Duration 表示基于纳秒的时间量,between 计算差值。适合度量耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.time.Duration
import java.time.Instant
fun main() {
val start = Instant.now()
// 业务处理...
val end = Instant.now()
// 耗时:
val elapsed = Duration.between(start, end)
println(elapsed.toMillis())
println(elapsed.toSeconds())
// 构造时长:
println(Duration.ofMinutes(5))
println(Duration.ofHours(2).plusMinutes(30))
// 比较:
println(elapsed.compareTo(Duration.ZERO))
}
// Duration 基于纳秒的时间量
// 适合度量耗时与时间差
// ofMinutes/ofHours/ofDays 便捷构造

Period 周期

Period 表示年/月/日差,between 计算年龄。ofWeeks/ofDays 构造周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.time.LocalDate
import java.time.Period
fun main() {
val born = LocalDate.of(1995, 5, 20)
val today = LocalDate.now()
// 年月日差值:
val age = Period.between(born, today)
println("${age.years}${age.months} 个月")
// 构造周期:
println(Period.of(1, 6, 0)) // 1 年 6 个月
println(Period.ofDays(30))
// 应用于日期:
println(today.plus(Period.ofWeeks(2)))
}
// Period 基于日期(年/月/日)
// Duration 基于时间(时/分/秒)
// 纯天数差值用 ChronoUnit.DAYS.between

带时区时刻

ZonedDateTime 含时区。withZoneSameInstant 切换时区。存储用 UTC 展示转本地。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.time.ZonedDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
fun main() {
// 带时区时刻:
val now = ZonedDateTime.now()
println(now)
// 指定时区:
val tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"))
println(tokyo)
// 时区切换:
val ny = now.withZoneSameInstant(
ZoneId.of("America/New_York")
)
println(ny)
// 格式化带时区:
println(now.format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z")
))
}
// ZonedDateTime = LocalDateTime + ZoneId
// 存储用 UTC(Instant),展示转本地时区
// 夏令时切换由库自动处理

17.进程与系统

命令行参数、环境变量、ProcessBuilder 执行外部命令、系统信息与关闭钩子。

参数与环境

main 的 args 收命令行参数。System.getenv 读环境变量,getProperty 读系统属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
fun main(args: Array<String>) {
// 命令行参数:
println("参数个数: ${args.size}")
args.forEachIndexed { i, a -> println("$i: $a") }
// 环境变量:
println(System.getenv("PATH"))
println(System.getenv().size)
// 系统属性:
println(System.getProperty("user.home"))
}
// 运行:kotlin MainKt 参数1 参数2
// 常用属性:user.home / os.name / java.version
// 属性不存在返回 null

ProcessBuilder

ProcessBuilder 执行外部命令,directory 设工作目录。redirectErrorStream 合并错误流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.io.File
fun main() {
// 执行外部命令:
val pb = ProcessBuilder("ls", "-l")
.directory(File("."))
.redirectErrorStream(true)
val process = pb.start()
// 等待并读取输出:
val output = process.inputStream.bufferedReader()
.readText()
println(output)
println("退出码: ${process.waitFor()}")
}
// ProcessBuilder 比 Runtime.exec 更安全
// redirectErrorStream 合并错误流
// 命令参数分开传,勿拼字符串(防注入)

退出码

waitFor 阻塞等待子进程。退出码 0 表示成功。exitProcess 设置当前进程退出码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.File
fun main() {
// 启动子进程:
val pb = ProcessBuilder("cmd", "/c", "echo hi")
val p = pb.start()
// 非阻塞检查:
println("已启动: ${p.isAlive}")
// 阻塞等待:
val code = p.waitFor()
println("退出码: $code")
}
// 退出码 0 表示成功
// exitProcess(1) 立即退出当前进程
// 注意 exitProcess 跳过 finally 清理

捕获子进程输出

读取 inputStream 获取子进程输出。waitFor 带超时防卡死。destroyForcibly 强杀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.File
import java.util.concurrent.TimeUnit
fun main() {
// 捕获标准输出:
val p = ProcessBuilder("java", "-version")
.redirectErrorStream(true)
.start()
val out = p.inputStream.bufferedReader()
.use { it.readText() }
println("输出:\n$out")
// 超时等待:
if (p.waitFor(5, TimeUnit.SECONDS)) {
println("退出码: ${p.exitValue()}")
} else {
p.destroyForcibly()
println("超时,已终止")
}
}
// 大输出用按行读取避免内存占用
// 防止子进程阻塞:读流与等待同时进行
// destroyForcibly 强杀进程

系统信息

读取 JVM 与操作系统属性。Runtime 查看内存。用于日志与诊断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
// JVM 信息:
println("Java: ${System.getProperty("java.version")}")
println("厂商: ${System.getProperty("java.vendor")}")
println("系统: ${System.getProperty("os.name")}")
println("架构: ${System.getProperty("os.arch")}")
// 内存:
val runtime = Runtime.getRuntime()
println("最大内存: ${runtime.maxMemory() / 1024 / 1024}MB")
println("已用内存: ${(runtime.totalMemory() - runtime.freeMemory()) / 1024 / 1024}MB")
// 工作目录:
println("工作目录: ${System.getProperty("user.dir")}")
}
// 处理器核数:
// Runtime.getRuntime().availableProcessors()
// 生产监控用 jcmd / JMX

关闭钩子

addShutdownHook 注册 JVM 退出时执行的任务。用于清理资源与保存状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import kotlin.concurrent.thread
fun main() {
// 注册关闭钩子:
Runtime.getRuntime().addShutdownHook(
thread(start = false) {
println("清理资源...")
// 保存状态、关闭连接
}
)
// 业务逻辑:
println("程序运行中")
Thread.sleep(2000)
println("退出")
}
// 钩子在 JVM 正常退出时执行
// 不保证在所有强制退出场景执行
// kill -9 无法触发钩子

实时读取输出

按行读取子进程输出流,实时处理进度。useLines 自动关闭流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.io.File
fun main() {
// 分步读取子进程输出:
val p = ProcessBuilder(
"ping", "-n", "2", "127.0.0.1"
)
.redirectErrorStream(true)
.start()
// 按行实时读取:
p.inputStream.bufferedReader().useLines { lines ->
lines.forEach { println("进度: $it") }
}
println("退出码: ${p.waitFor()}")
}
// 实时读取避免缓冲溢出
// useLines 自动关闭输入流
// 大数据量逐行处理内存友好

终止进程

destroy 温和终止,destroyForcibly 强杀。超时未结束则强制终止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.File
import java.util.concurrent.TimeUnit
fun main() {
val p = ProcessBuilder(
"ping", "127.0.0.1", "-t"
)
.redirectErrorStream(true)
.start()
// 超时后终止:
val done = p.waitFor(3, TimeUnit.SECONDS)
if (!done) {
println("超时,终止进程")
p.destroy() // 温和终止
p.waitFor()
println("退出码: ${p.exitValue()}")
}
}
// destroy 发终止信号,子进程可响应
// destroyForcibly 强杀,子进程无机会清理
// Windows 用 taskkill /F 强杀进程树

18.正则表达式

Regex 基础、匹配查找、捕获分组、替换分割、常用模式与标志选项。

Regex 基础

Regex 创建正则对象。matches 全匹配,containsMatchIn 判断包含,matchEntire 整串匹配。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
// 创建正则:
val email = Regex("^\\w+@\\w+\\.\\w+$")
println(email.matches("[email protected]")) // true
println(email.matches("not-an-email")) // false
// containsMatchIn 判断包含:
println(Regex("\\d+").containsMatchIn("有 123 个"))
// matchEntire 全匹配:
val num = Regex("-?\\d+\\.?\\d*")
println(num.matchEntire("3.14") != null)
}
// Regex 线程安全,可复用为 val
// 注意反斜杠转义:\\d 表示 \\d 到 \\d
// 用 raw string """ 减少转义

字面量与选项

Regex.escape 转义特殊字符。IGNORE_CASE 忽略大小写,MULTILINE 多行模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import kotlin.text.RegexOption
fun main() {
// 字面量匹配(特殊字符按原样):
val literal = Regex.escape("a.b*c")
println(literal) // a\\.b\\*c
println(Regex(literal).matches("a.b*c")) // true
// 大小写不敏感:
val ci = Regex("kotlin", RegexOption.IGNORE_CASE)
println(ci.matches("KOTLIN")) // true
// 组合选项:
val multi = Regex("^a$", setOf(
RegexOption.MULTILINE,
RegexOption.IGNORE_CASE,
))
println(multi.matches("A"))
}
// 元字符:. * + ? ( ) [ ] { } ^ $ |
// 匹配特殊字符前先 Regex.escape
// 选项:IGNORE_CASE / MULTILINE / DOT_MATCHES_ALL

查找匹配

find 返回第一个匹配,findAll 返回全部匹配。MatchResult 可访问值、分组与区间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val text = "id=42 和 id=17 和 id=8"
val re = Regex("id=(\\d+)")
// 第一个匹配:
val first = re.find(text)
println(first?.value) // id=42
println(first?.groupValues) // [id=42, 42]
// 全部匹配:
val all = re.findAll(text)
all.forEach { m -> println(m.value) }
// 区间:
println(first?.range) // 0..4
}
// find 返回第一个 MatchResult
// findAll 返回序列(惰性)
// groupValues[0] 全文,[1..] 分组

捕获分组

括号定义捕获组。groupValues 按序号取值,命名分组用 groups["name"]。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val text = "2026-08-02 15:30:00"
val re = Regex("(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})")
val m = re.find(text)!!
// 分组取值:
println(m.groupValues[1]) // 2026
println(m.groupValues[2]) // 08
// 命名分组:
val named = Regex("(?<year>\\d{4})-(?<month>\\d{2})")
val nm = named.find("2026-08")!!
println(nm.groups["year"]?.value) // 2026
println(nm.groups["month"]?.value) // 08
}
// 非捕获分组:(?:...)
// 惰性匹配:(.*?)
// 解构匹配:val (y, m, d) = m.destructured

正则替换

replace 替换所有匹配,replaceFirst 只替换首个。替换闭包可访问捕获组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main() {
val text = "2026-08-02 15:30"
// 替换匹配内容:
println(Regex("\\d+").replace(text, "#"))
// 首次替换:
println(Regex("\\d+").replaceFirst(text, "X"))
// 用匹配内容生成替换:
val date = Regex("(\\d{4})-(\\d{2})-(\\d{2})")
println(date.replace(text) { m ->
val (y, mo, d) = m.destructured
"$d/$mo/$y"
})
}
// replace 处理所有匹配
// 替换闭包可访问分组值
// m.destructured 解构分组

正则分割

split(Regex) 按正则切分字符串。比字符串分割更灵活,支持多分隔符。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
val text = "1, 2;3|4"
// 正则分割:
println(text.split(Regex("[,;|]+")))
println(text.split(Regex("\\s*[,;|]\\s*")))
// 提取键值:
val log = "A:1 B:2 C:3"
println(Regex("(\\w+):(\\d+)").findAll(log)
.map { it.destructured.toList() }
.toList())
}
// split 默认不保留分隔符
// 正则分割比字符串分割更灵活
// 大文本用 findAll 惰性处理

常用正则

邮箱、手机号、IPv4、中文字符与 URL 的常用正则模板。按需调整边界。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 邮箱:
val EMAIL = Regex("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$")
// 手机号(中国大陆):
val CN_PHONE = Regex("^1[3-9]\\d{9}$")
// IPv4:
val IPV4 = Regex("^(\\d{1,3}\\.){3}\\d{1,3}$")
// 中文字符:
val CHINESE = Regex("[\\u4e00-\\u9fa5]")
// URL:
val URL = Regex("^https?://[^\\s]+")
fun main() {
println(EMAIL.matches("[email protected]"))
println(CN_PHONE.matches("13800138000"))
println(IPV4.matches("192.168.1.1"))
println(CHINESE.containsMatchIn("Hello 世界"))
println(URL.matches("https://example.com/x"))
}
// 复杂规则建议用专门库或状态机
// 验证与提取分开:先匹配再取分组
// 正则匹配可能有 ReDoS 风险,注意长度

标志选项

RegexOption 常量控制匹配行为。内联标志 (?i)(?m)(?s) 可写在模式里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import kotlin.text.RegexOption
fun main() {
// 选项常量:
println(RegexOption.IGNORE_CASE) // 忽略大小写
println(RegexOption.MULTILINE) // 多行模式
println(RegexOption.DOT_MATCHES_ALL) // . 匹配换行
// 内联标志:
val inline = Regex("(?i)abc")
println(inline.matches("ABC"))
// 组合使用:
val combined = Regex("^\\w+$", RegexOption.IGNORE_CASE)
println(combined.matches("Hello123"))
}
// (?i) 忽略大小写 (?m) 多行 (?s) 单行
// 标志位作为参数比内联更清晰
// COMMENT 选项允许正则内注释与空白

19.构建与依赖

Gradle Kotlin DSL、插件、依赖、测试、协程与序列化库配置。

Gradle 基础

build.gradle.kts 用 Kotlin DSL 配置构建。mainClass 指定运行入口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 文件:build.gradle.kts(Kotlin DSL)
plugins {
kotlin("jvm") version "2.0.21"
application
}
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test"))
}
application {
mainClass.set("MainKt")
}
// 运行:./gradlew run
// 打包:./gradlew build
// 目录约定:
// src/main/kotlin/ 源码
// src/test/kotlin/ 测试
// build/libs/ 构建产物

Kotlin 插件

kotlin("jvm") 声明 JVM 项目。jvmToolchain 指定 JDK 版本。Wrapper 固定 Gradle。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 插件声明:
plugins {
kotlin("jvm") version "2.0.21"
}
// 常用插件:
// kotlin("jvm") JVM 项目
// kotlin("multiplatform") 多平台
// kotlin("kapt") 注解处理
// 设置 JDK 工具链:
kotlin {
jvmToolchain(21)
}
// Wrapper 固定 Gradle 版本:
// ./gradlew wrapper --gradle-version 8.10
// 多模块在 settings.gradle.kts 中 include

依赖配置

implementation/testImplementation/compileOnly 等配置控制依赖作用域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dependencies {
// 运行时依赖:
implementation(
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0"
)
// 测试依赖:
testImplementation("junit:junit:4.13.2")
// 编译期依赖:
compileOnly(
"jakarta.servlet:jakarta.servlet-api:6.0.0"
)
}
// 依赖配置说明:
// implementation 运行时与编译期可见
// testImplementation 仅测试
// compileOnly 仅编译期(不打包)
// runtimeOnly 仅运行期
// api 暴露给使用方(library 项目)

运行与分发

application 插件提供 run/installDist 任务。run --args 传命令行参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
plugins {
application
}
application {
mainClass.set("MainKt")
}
// 命令行运行:
// ./gradlew run 运行主类
// ./gradlew run --args="参数1 参数2"
// ./gradlew installDist 生成可分发脚本
// 指定运行 JVM 参数:
tasks.named<JavaExec>("run") {
jvmArgs("-Xmx512m")
}
// 输出位置:build/install/<project>/bin/

测试

kotlin.test 提供断言。testImplementation 引入测试库。test 任务运行全部测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import kotlin.test.Test
import kotlin.test.assertEquals
// 被测函数(src/main/kotlin):
fun add(a: Int, b: Int) = a + b
// 测试类(src/test/kotlin/CalculatorTest.kt):
class CalculatorTest {
@Test
fun add_returns_sum() {
assertEquals(5, add(2, 3))
}
}
// 运行测试:
// ./gradlew test
// 常见断言:
// assertEquals / assertTrue / assertNull
// assertFailsWith<IllegalArgumentException> { ... }
// 测试报告:build/reports/tests/test/

协程依赖

kotlinx-coroutines-core 提供协程核心。Android/Swing 版本提供对应主线程调度器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
dependencies {
implementation(
"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0"
)
// Android 或 JVM Swing 主线程:
implementation(
"org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
)
}
// 各模块:
// kotlinx-coroutines-core 核心(协程、Flow、Channel)
// kotlinx-coroutines-android Android Dispatchers.Main
// kotlinx-coroutines-swing Swing 主线程
// 核心用法:
import kotlinx.coroutines.*
fun demo() = runBlocking {
launch { delay(100); println("完成") }
}
// 版本需与 Kotlin 版本兼容

序列化依赖

kotlin plugin.serialization 插件加 kotlinx-serialization-json 库实现 JSON 编解码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
plugins {
kotlin("jvm") version "2.0.21"
kotlin("plugin.serialization") version "2.0.21"
}
dependencies {
implementation(
"org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
)
}
// 使用:
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
@Serializable
data class User(val name: String, val age: Int)
fun main() {
println(Json.encodeToString(User("A", 1)))
}
// 序列化插件自动生成编解码器
// 需同时加插件与运行时库
// 其他格式:kotlinx-serialization-protobuf 等

打包发布

distribution 插件生成可分发压缩包。publishToMavenLocal 发布到本地仓库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 生成可分发包:
plugins {
application
distribution
}
application {
mainClass.set("MainKt")
}
// 运行:
// ./gradlew distTar 生成 tar 压缩包
// ./gradlew distZip 生成 zip 压缩包
// ./gradlew installDist 解包到 build/install
// 产物含启动脚本与 lib/ 依赖
// 发布到本地 Maven 仓库:
// ./gradlew publishToMavenLocal
// maven-publish 插件配置:
plugins {
`maven-publish`
}
publishing {
publications {
create<MavenPublication>("maven") {
from(components["java"])
}
}
}
关于本速查

本页是 Kotlin 2.0 的自包含速查手册,覆盖语言核心与最常用标准库在真实项目中约 80% 的常见用法。内容偏向现代惯用法:空安全(可空类型 ?、安全调用 ?.、Elvis ?:)、data class 与 sealed class、作用域函数、扩展函数,以及协程与 Flow 的异步并发模型。权威参考见官方 Kotlin 文档与 Kotlin in Action。 19 个章节各自聚焦一个主题——从第一个程序到协程、空安全与常见误区。每节拆成 8–14 个带示例的小节(每个 5–20 行),共约 160 个主题。代码片段刻意短小、自解释,注释为简体中文。 所有处理都在浏览器中完成——无上传、无追踪。本页是 GuruToolkit 免费开发者工具集的一部分;代码片段可自由使用,无任何担保。

版本 2.1.0