Kotlin 语言【知识点整理2】

📅 发布时间:2026/8/25 16:36:33
Kotlin 语言【知识点整理2】 目录一、基本概念1.包的定义与导入2.程序入口点2.1 输入3.变量二、基本类型1.数字1.1 整数类型1.2 浮点类型1.3 数字字面常量1.4 装箱与缓存1.4.1 JVM是怎么存储数字的1.4.2 使用可空类型的时候会触发装箱操作1.4.3 JVM 对 -128 到 127 的小数字做了特殊优化1.5 显式数字转换案例1整数与浮点数案例2数字超范围1.6 数字运算1.7 整数除法2.布尔3.字符3.1 特殊字符4.字符串4.1 基础特性4.2 常用操作4.3 扩展函数增强4.4 性能优化技巧4.5 与Java互操作三、控制语句1.条件语句if 与 when2.循环语句2.1 遍历范围Range2.2 遍历数组2.3 遍历集合List/Set2.4 遍历 Map2.5 遍历字符串字符2.6 自定义迭代器2.7 控制流操作2.8 标签控制2.9 与 Java 对比四、函数1.函数的定义2.扩展函数2.1 使用案例3.高阶函数3.1 语法3.2 使用案例五、空安全机制1.问号?的作用与用法2.为什么需要这些符号3.最佳实践示例3.1 安全处理空值4.关键总结一、基本概念1.包的定义与导入包的声明应处于源文件顶部package my.demo //包名 import kotlin.text.* //引用某个包2.程序入口点Kotlin 应用程序的入口点是 main 函数fun main() { println(Hello world!) }main 的另一种形式接受可变数量的 String 参数当程序需要处理命令行参数时可以使用这个形式感兴趣可自行扩展如读取文件名、配置选项等fun main(args: ArrayString) { println(args.contentToString()) }2.1 输入// Prints a message to request input println(请输入: ) // 这里使用了一个不可变的youword参数读取并存储用户输入 val yourWord readln() // 显示你输入的信息 print(你输入了: ) print(yourWord)3.变量kotlin具有强大的类型推断我们可以让编译器通过推断变量的类型来完成工作。//声明一个可变变量并初始化它 var a: String initial println(a) //声明一个不可变变量并初始化它。 val b: Int 1 //声明不可变变量并在不指定类型的情况下对其进行初始化。编译器推断Int类型。 val c 3变量必须初始化后才可以使用否则会报错只要在第一次使用它之前做初始化就可以//声明一个没有初始化的变量。 var e: Int //尝试使用该变量会导致编译器错误变量‘e’必须初始化。 println(e) //初始化后再用 e 666二、基本类型Kotlin 中使用的类型数字、布尔、字符、字符串、数组。1.数字1.1 整数类型Kotlin 提供了一组表示数字的内置类型。 对于整数有四种不同大小与取值范围的类型类型大小比特数最小值最大值Byte8-128127Short16-3276832767Int32-2,147,483,6482,147,483,647Long64-9,223,372,036,854,775,8089,223,372,036,854,775,807当初始化一个没有显式指定类型的变量时编译器会自动推断为自 Int 起足以表示该值的最小类型。 如果不超过 Int 的取值范围那么类型是 Int。 如果超出了该范围那么类型是 Long。 如需显式指定 Long 值请给该值追加后缀 L。 显式指定类型会触发编译器检测该值不会超出指定类型的取值范围。val one 1 // Int val threeBillion 3000000000 // Long val oneLong 1L // Long val oneByte: Byte 11.2 浮点类型对于实数Kotlin 提供了浮点类型Float单精度与Double双精度两种类型类型大小比特数10进制位数最小值最大值Float326-73.4028235 × 10³⁸1.7976931348623157 × 10³⁰⁸Double6415-161.4 × 10⁻⁴⁵4.9 × 10⁻³²⁴对于以小数初始化的变量编译器会推断为 Double 类型val pi 3.14 // Double val one: Double 1 //Double val oneDouble 1.0 // Double如需将一个值显式指定为 Float 类型请添加 f 或 F 后缀。 如果以这种方式提供的值包含多于 7 位十进制数那么会将其舍入val e 2.7182818284 // Double val eFloat 2.7182818284f // Float实际值为 2.7182817与一些其他语言不同Kotlin 中的数字没有隐式拓宽转换。 例如具有 Double 参数的函数只能对 Double 值调用而不能对 Float、 Int 或者其他数字值调用fun main() { //sampleStart fun printDouble(x: Double) { print(x) } val x 1.0 val xInt 1 val xFloat 1.0f printDouble(x)//参数类型匹配 printDouble(xInt) // 参数类型不匹配 printDouble(xFloat) // 参数类型不匹配 //如果需要让xInt或者xFloat使用这个函数需要先进行显式类型转换 xInt.toDouble() }1.3 数字字面常量数值常量直接在代码中表示固定值的符号字面值有以下几种:十进制123Long 类型以大写 L 结尾123L十六进制0x0F二进制0b00001011Kotlin 不支持八进制。Kotlin 同样支持浮点数的常规表示方法:Doubles (default when the fractional part does not end with a letter): 123.5, 123.5e10Float以字母 f 或 F 结尾123.5f你可以使用下划线使数字常量更易读val oneMillion 1_000_000 //实际值仍然是 1000000一百万,下划线 _ 作为视觉分隔符使数字更易读.在编译阶段会被完全忽略不影响实际值 val creditCardNumber 1234_5678_9012_3456L val socialSecurityNumber 999_99_9999L val hexBytes 0xFF_EC_DE_5E val bytes 0b11010010_01101001_10010100_10010010 val bigFractional 1_234_567.71828182841.4 装箱与缓存1.4.1 JVM是怎么存储数字的基本类型int、double 等直接存数值高效装箱类型Integer、Double 等存对象用于特殊场景1.4.2 使用可空类型的时候会触发装箱操作Int? 中的问号?表示可空类型Nullable Type这是 Kotlin 类型系统中最重要的特性之一Kotlin 通过问号语法强制开发者显式处理 null 的情况避免 NullPointerExceptionval basic: Int 10 // 基本类型高效 不可为 null 的整数等价于 Java 的 int 基本类型 val boxed: Int? 10 // 装箱类型对象 可以为 null 的整数等价于 Java 的 Integer 包装类但更安全1.4.3 JVM 对 -128 到 127 的小数字做了特殊优化这些数字装箱后会共用同一个缓存对象超出范围的数字每次装箱都会创建新对象//假如是1个127以内的数jvm会对其进行缓存哪怕装箱以后也都会指向同一个对象 val b: Int 100 val boxedB: Int? b // 装箱 val anotherBoxedB: Int? b // 再次装箱 println(boxedB anotherBoxedB) // 因为100没超出缓存范围两个引用指向同一个对象 输出true println(boxedB anotherBoxedB) // 比较的是数值内容两者都是100 输出true //但如果b是的范围不属于 -128 到 127那么就不再是同一个对象 val b: Int 10000 println(boxedB anotherBoxedB) // 因为10000超出缓存范围每次装箱都创建新对象 输出false注意这里只是借助可空类型以及来说明装箱后的缓存现象除非必要不要用 Int?一定是优先使用基本类型始终使用 比较数字而不是使用比较内存地址因为这似乎没啥意义1.5 显式数字转换(1)较小的类型不会隐式地转换为较大的类型反之亦然。例如将Byte类型的值赋给Int变量需要显式转换fun main() { val byte: Byte 1 val intAssignedByte: Int byte //会报错 val intConvertedByte: Int byte.toInt()//需要显示转换 println(intConvertedByte) }所有数字类型都支持转换为其他类型只需要调用数字下的 toxxx函数方法toByte()、toShort()、toInt()、toLong()、toFloat()、toDouble()。(2) 也有很多不需要显式类型转换的情况因为程序会根据上下文推断类型 而且算术运算符也会有重载自动处理类型转换。例如fun main() { // 场景1根据赋值自动推断 val name Kotlin // 自动推断为 String val score 95 // 自动推断为 Int val ratio 0.9 // 自动推断为 Double //场景2算术运算符重载处理 val l 1L 3 // Long Int 得到的是Long println(l is Long) // 使用is判断l的类型 输出true }Kotlin 刻意设计为不支持隐式类型转换Implicit Conversions这与 Java、C 等语言不同。以下是关键原因和典型案例案例1整数与浮点数val price: Double 10 // 编译错误必须显式转换 // 正确做法 val correctPrice: Double 10.toDouble()10 是 Int 类型直接赋值给 Double 会导致精度意外丢失的风险。Kotlin 强制要求开发者明确转换意图。对比 Java 的隐式转换double price 10; // Java 自动隐式转换可能掩盖潜在问题案例2数字超范围val bigNumber: Long 10000000000 // 编译错误超出 Int 范围 // 必须显式声明为 Long val correctNumber 10000000000L设计哲学Kotlin 要求开发者明确知晓自己在处理大数字避免数值溢出等隐蔽问题。1.6 数字运算Kotlin支持数字运算的标准集、 -、 *、 /、 %fun main() { println(1 2) println(2_500_000_000L - 1L) println(3.14 * 2.71) println(10.0 / 3) }1.7 整数除法整数间的除法总是返回整数。会丢弃任何小数部分。fun main() { val x 5 / 2 println(x 2.5) //会提示操作符‘’不能应用于‘Int’和‘Double’ println(x 2) // 只保留整数部分 输出true }对于任何两个整数类型之间的除法来说都是如此fun main() { val x 5L / 2 println (x 2) //提示错误因为x此时是Long类型因为不能将Long (x)与Int(2)进行比较 println(x 2L) // true }如需返回带小数部分的除法结果请将其中的一个参数显式转换为浮点类型fun main() { val x 5 / 2.toDouble() //这种做法会让x被推测为Double类型 println(x 2.5) }2.布尔Boolean 类型表示可以有 true 与 false 两个值的布尔对象fun main() { val myTrue: Boolean true val myFalse: Boolean false val boolNull: Boolean? null println(myTrue || myFalse) // true println(myTrue myFalse) // false println(!myTrue) // false println(boolNull) // null }||和操作符是惰性的这意味着如果第一个操作数为真||运算符不计算第二个操作数。如果第一个操作数为假操作符不对第二个操作数求值。3.字符字符用 Char 类型表示。 字符字面值用单引号括起来: 13.1 特殊字符特殊字符可以以转义反斜杠 \开始。 支持这几个转义序列\t——制表符\b——退格符\n——换行LF\r——回车CR\——单引号\——双引号\\——反斜杠\$——美元符如果需要编码其他字符也可以用 Unicode 转义序列语法\uFF00。fun main() { val aChar: Char a println(aChar) println(\n) // 输出一个额外的换行符 println(\uFF00) }如果字符变量的值是数字也可以使用 digitToInt() 函数将其显式转换为 Int 数字。4.字符串Kotlin 中字符串用 String 类型表示既保留了 Java 的强大功能又通过扩展函数和语言特性大幅提升了易用性4.1 基础特性(1)不可变性与 Java 相同Kotlin 字符串是不可变的val s hello s[0] H // 编译错误String不可变(2)两种字面量转义字符串支持转义字符val path C:\\Windows\\System32原始字符串Raw String三引号包裹保留所有格式val json { name: Kotlin, age: 10 } .trimIndent()4.2 常用操作比 Java 更强大的插值功能val name Alice val greeting Hello, $name! // 插入简单变量 val lengthInfo Name length: ${name.length} // 插入表达式 println(1 2 ${1 2}) // 输出1 2 3拼接与重复val combined Hello World // 传统方式 val repeated Na.repeat(3) Batman! // 输出NaNaNa Batman!多行字符串处理val poem |床前明月光 |疑是地上霜。 |举头望明月 |低头思故乡。 .trimMargin(|) // 自动去除前导|和缩进分割与连接val langs Kotlin,Java,Swift.split(,) // 拆分为List val merged langs.joinToString( | ) // 合并为Kotlin | Java | Swift4.3 扩展函数增强除了常用操作以外Kotlin 为字符串添加了大量实用扩展展示部分感兴趣可以查API文档或者问AI函数作用示例substringBefore/After截取子串file.txt.substringBefore(.) → filepadStart/padEnd填充对齐5.padStart(2, 0) → 05toIntOrNull安全转换123.toIntOrNull() → 123isBlank空值检查 .isBlank() → truereplace正则替换123-456.replace(Regex(\\D), ) → 1234564.4 性能优化技巧字符串构建器val sb buildString { append(Hello) append( ) append(World) } // 比连续拼接效率更高避免重复创建// 错误做法每次循环创建新对象 var result for (i in 1..1000) { result i.toString() } // 正确做法 val betterResult StringBuilder().apply { for (i in 1..1000) { append(i) } }.toString()4.5 与Java互操作无缝调用Java字符串方法val javaStyle Kotlin.toLowerCase(Locale.ROOT)消除空指针隐患val maybeString: String? getJavaString() val safeLength maybeString?.length ?: 0 // 安全访问 //输出结果如下 //当getJavaString() 返回 null 时 → safeLength 0 //当返回空串 时 → safeLength 0 //当返回有效字符串时 →safeLength 字符串长度三、控制语句kotlin拥有比Java更智能的流程控制1.条件语句if 与 whenKotlin 的if 不仅是语句还可以是表达式有返回值返回值由每个分支的最后一行的表达式决定注意这种表达式型的写法 if 必须有 else 分支否则会报错Kotlin 的 if 是表达式因此它天然就会产生值无论是否被接比如下面的表达式有变量接受返回值就有意义。所以当需要返回值时必须显式指定接收位置没有变量接受返回值就没有意义直接丢弃// 作为表达式返回最大值 val max if (a b) { println(a 更大) a // 最后一行的表达式会自动成为该分支的返回值 } else { b } // 替代 Java 的三元运算符 val status if (score 60) 通过 else 未通过when是增强版switch支持任意类型和复杂条件//常量值匹配类似于switch when (status) { 200 - println(成功) 404 - println(未找到) 500 - println(服务器错误) else - println(未知状态) } //类型检查、智能转换使用 is 或 !is 来检查对象的类型并且在分支内自动智能转换为该类型 when (shape) { is Circle - println(半径: ${shape.radius}) // 自动转为 Circle is Square - println(边长: ${shape.side}) // 自动转为 Square !is Int - println(不是整数) } //范围匹配 when (score) { in 90..100 - println(A) in 80..89 - println(B) !in 60..100 - println(不及格) else - println(未知类型) } // 作为表达式使用直接输出结果并且使用变量接收 val description when { temperature 30 - 炎热 temperature 10 - 寒冷 else - 舒适 }2.循环语句kotlin的循环语句简介且强大2.1 遍历范围Range// 基础、常规 for (i in 1..5) { print($i ) // 输出: 1 2 3 4 5 } // 反向 for (i in 5 downTo 1) { print($i ) // 输出: 5 4 3 2 1 } // 带步长 for (i in 1..10 step 2) { print($i ) // 输出: 1 3 5 7 9 } // 半开范围不包含结束值 for (i in 1 until 5) { print($i ) // 输出: 1 2 3 4 }2.2 遍历数组val numbers arrayOf(10, 20, 30) // 遍历元素 for (num in numbers) { print($num ) // 输出: 10 20 30 } // 带索引遍历 for (index in numbers.indices) { println(索引 $index 的值是 ${numbers[index]}) } // 输出: // 索引 0 的值是 10 // 索引 1 的值是 20 // 索引 2 的值是 302.3 遍历集合List/Setval fruits listOf(Apple, Banana, Orange) // 基础遍历 for (fruit in fruits) { print($fruit ) // 输出: Apple Banana Orange } // 带索引遍历 //val iterable fruits.withIndex() 是kotlin当中的一个可迭代对象 //当我们在for循环中使用了iterable对象实际上是发生了下面这几件事 1、隐式调用iterable.iterator()内部会产生index 2、循环开始时检查iterator.hasNext() 3、每次迭代获取item iterator.next() for ((index, fruit) in fruits.withIndex()) { println($index: $fruit) } // 输出: // 0: Apple // 1: Banana // 2: Orange2.4 遍历 Mapval map mapOf(a to 1, b to 2, c to 3) // 遍历键值对 for ((key, value) in map) { println($key - $value) } // 输出: // a - 1 // b - 2 // c - 3 // 仅遍历键 for (key in map.keys) { print($key ) // 输出: a b c } // 仅遍历值 for (value in map.values) { print($value ) // 输出: 1 2 3 }2.5 遍历字符串字符val text Kotlin // 遍历字符 for (char in text) { print($char-) // 输出: K-o-t-l-i-n- } // 带索引遍历 for ((i, char) in text.withIndex()) { println($i: $char) } // 输出: // 0: K // 1: o // 2: t // 3: l // 4: i // 5: n2.6 自定义迭代器系统提供的功能能应对多数场景但kotlin也提供了自定义迭代器实现Iterable接口的类可被遍历对自定义迭代器感兴趣后续可自行扩展学习//定义了一个名为 Counter 的自定义迭代器类它实现了 IterableInt 接口 class Counter(val max: Int) : IterableInt { override fun iterator(): IteratorInt object : IteratorInt { private var current 0 // 当前计数值 // 检查是否还有下一个值 override fun hasNext() current max // 获取下一个值并移动 override fun next() current } } // 使用时指定max的值是3然后自定义迭代器就会根据你定义的条件进行遍历 for (i in Counter(3)) { print($i ) // 输出: 0 1 2 }2.7 控制流操作使用continue、break进行流程控制// 跳过当前迭代continue for (i in 1..5) { if (i 3) continue print($i ) // 输出: 1 2 4 5 } // 中断循环break for (i in 1..10) { if (i 5) break print($i ) // 输出: 1 2 3 4 5 } // 标签跳转控制外层循环 outer for (i in 1..3) { for (j in 1..3) { if (i 2 j 2) breakouter print(($i,$j) ) } } // 输出: (1,1) (1,2) (1,3) (2,1)2.8 标签控制精准控制循环跳转outer for (i in 1..3) { inner for (j in 1..3) { if (i 2 j 2) breakouter print(($i,$j) ) // 输出: (1,1) (1,2) (1,3) (2,1) } }2.9 与 Java 对比特性JavaKotlin优势条件返回值三元运算符 ab ? a : bif (ab) a else b支持多行逻辑多分支条件switch-casewhen 表达式支持任意类型和复杂条件范围循环for (int i0; i10; i)for (i in 0 until 10)避免索引越界错误四、函数1.函数的定义函数声明​使用fun关键字定义fun add(a: Int, b: Int): Int { return a b }参数格式参数名: 类型返回值类型在参数后声明如 : Int表达式函数​​单表达式省略 {} 和 returnfun add(a: Int, b: Int): Int a b // 类型可自动推断进一步简化 fun add(a: Int, b: Int) a b参数支持默认值fun greet(name: String Guest) { println(Hello, $name!) }调用greet() 输出 Hello, Guest!调用时,明确参数名fun createUser(name: String, age: Int, isAdmin: Boolean false) { ... } // 指定参数名可乱序 createUser(age 25, name Alice)可变参数​​类似 Java 的 ...fun sum(vararg numbers: Int): Int { return numbers.sum() } sum(1, 2, 3) // 输出 6顶层函数​​可脱离类直接定义在文件中// File: MathUtils.kt fun multiply(a: Int, b: Int) a * b无需像 Java 的静态工具类。局部函数内部嵌套函数fun outer() { fun inner() { println(Inner) } inner() }2.扩展函数扩展函数Extension Functions​​ 是一种在不修改原类定义的情况下为现有类添加新功能的机制。它们通过静态解析实现不会侵入原类代码但能像类成员一样被调用。语法结构fun ReceiverType.functionName(params): ReturnType { // 函数体 // 通过 this 访问接收者对象可省略 }ReceiverType: 要扩展的类接收者类型functionName: 扩展函数的名称this: 在函数体内指向接收者对象实例隐式可用本质是扩展函数是静态解析的静态函数编译时确定不会修改原类而是通过编译器转换为静态方法调用。例如// 编译后实际会生成 fun String.lastChar() → public static char lastChar(String receiver)2.1 使用案例为String扩展一个函数获取o在字符串中第一次出现的位置/** * 获取o在字符串中第一次出现的位置如果没出现就返回-1 */ fun String.getOIndex(): Int { // 函数体 // 如果在这里面调用thisthis 指代调用该方法的字符串对象另外this也可以被省略 return this.indexOf(o) } //调用 val s :String hello val oIndex s.getOIndex() println(o出现的位置是$oIndex)View扩展简化 Android 中的视图控制fun View.show() { this.visibility View.VISIBLE } fun View.hide() { this.visibility View.GONE } // 使用 myButton.show() // 等价于 myButton.visibility View.VISIBLE可空类型扩展安全处理空值fun String?.safeLength(): Int { return this?.length ?: 0 // 若为 null 返回 0 } fun main() { val s: String? null println(s.safeLength()) // 输出0不会抛 NPE }3.高阶函数高阶函数 是指可以接受函数作为参数、或者返回一个函数的函数。将行为作为参数传递使代码更灵活且可复用同时保持类型安全。极大地增强了代码的抽象能力和灵活性。3.1 语法// 定义高阶函数 fun 函数名(参数名: (参数类型) - 返回类型) { // 调用传入的函数 参数名(实参) }3.2 使用案例基础回调函数把函数当成参数传递使用// 定义高阶函数 fun performRequest( url: String, onSuccess: (response: String) - Unit, // 函数类型参数Unit表示函数不返回任何有意义的值类似于java的void onFailure: (exception: Exception) - Unit // 函数类型参数 ) { try { // 模拟网络请求 val response 响应数据: $url onSuccess(response) // 调用成功回调 } catch (e: Exception) { onFailure(e) // 调用失败回调 } } // 使用 fun main() { performRequest( url https://api.example.com, onSuccess { response - println(成功: $response) }, onFailure { error - println(失败: ${error.message}) } ) }带返回值的函数参数// 定义高阶函数 fun transformData( data: ListInt, transform: (Int) - String // 接收Int返回String的函数 ): ListString { return data.map(transform) // 使用传入的转换函数 } // 使用 fun main() { val numbers listOf(1, 2, 3) val result transformData(numbers) { num - 数字: ${num * 2} // Lambda 表达式 } println(result) // 输出: [数字: 2, 数字: 4, 数字: 6] }返回函数的函数// 定义高阶函数返回一个函数 fun createLogger(prefix: String): (String) - Unit { return { message - // 返回一个Lambda表达式 println([$prefix] $message) } } // 使用 fun main() { val activityLogger createLogger(MainActivity) val networkLogger createLogger(Network) activityLogger(Activity 已创建) // 输出: [MainActivity] Activity 已创建 networkLogger(请求发送) // 输出: [Network] 请求发送 }返回类型(String) - Unit表示它返回一个函数这个返回的函数接受一个String参数返回Unit五、空安全机制在 Kotlin 中? 和 !!都是​​空安全机制​​的核心组成部分用于处理可能为 null 的值。这是 Kotlin 区别于 Java 的重要特性能有效减少 NullPointerException空指针异常。1.问号?的作用与用法声明可空类型​​var name: String? null // 声明一个可能为 null 的字符串不加 ? 的类型默认为​​非空类型​​不能赋 null 值var age: Int null // 编译错误Null can not be a value of a non-null type Int安全调用操作符?.​​val length: Int? text?.length // 如果 text 为 null返回 null 而不报错等价于 Java 的Integer length text ! null ? text.length() : null;安全转换as?安全的类型转换失败时返回 null 而非抛异常避免出现避免 ClassCastExceptionval num: Int? obj as? Int // 转换失败时返回 null 而不是抛异常Elvis 操作符?:左侧表达式为 null 时可以提供一个默认值简化空值处理​val name user?.name ?: Unknown // 若左边为 null则使用右侧默认值先计算 ?: 左侧表达式如果结果非空 - 直接返回该值如果结果为 null - 返回右侧表达式结果非空断言操作符这个慎用因为会导致空指针​告诉编译器我确定这个值不会为null哪怕真的为null你也可以直接抛空指针val length: Int text!!.length // 断言 text 不为 null若为 null 则立即抛 NPE2.为什么需要这些符号通过类型系统强制开发者显式处理空值将运行时可能出现的 NPE 转为编译期错场景JavaKotlin安全Kotlin危险访问可能为 null 的对象text.length()→ 可能 NPEtext?.length→ 返回 nulltext!!.length→ 可能 NPE​​优势​​编译时不检查空值​​编译时强制处理空值​​开发者主动承担风险3.最佳实践示例3.1 安全处理空值// 链式安全调用 Elvis 操作符 val cityName user?.address?.city ?: China // 配合 let 作用域函数 //为对象创建一个临时作用域在内部安全操作非空对象 user?.let { println(Name: ${it.name}) // 仅在非 null 时执行 }4.关键总结操作符含义风险程度?这可能为 null无风险?.为 null 时跳过无风险?:为 null 时使用默认值无风险!!​​我保证这里不为 null​​高风险终极建议90% 的场景使用 ?. 和 ?: 替代 !!剩余 10% 与 Java 交互的代码中使用 !! 前务必确认数据源的非空性永远不要为了快速完成代码而滥用 !!