首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

面试题 - Android - Kotlin相关内容

  • 25-04-25 13:21
  • 4246
  • 12202
juejin.cn

1. Kotlin 简介及特性

Kotlin 是一门由 JetBrains 开发的现代编程语言,可以在 JVM 上运行。主要特性包括:

  • 空安全(Null Safety)
  • 简洁的语法
  • 与 Java 100% 互操作
  • 支持函数式编程
  • 智能类型推断
  • 协程支持

2. @JvmOverloads 注解

@JvmOverloads 的作用是在有默认参数值的函数中自动生成重载函数。

kotlin
代码解读
复制代码
class Example { @JvmOverloads fun greet(name: String = "World", greeting: String = "Hello") { println("$greeting, $name!") } }

这会生成以下 Java 代码:

java
代码解读
复制代码
class Example { public void greet(String name, String greeting) { ... } public void greet(String name) { ... } public void greet() { ... } }

3. List 与 MutableList 区别

  • List: 不可变集合,只能读取
  • MutableList: 可变集合,可以进行增删改操作
kotlin
代码解读
复制代码
val readOnlyList: List = listOf("a", "b", "c") val mutableList: MutableList = mutableListOf("a", "b", "c") // 只能读取 readOnlyList[0] // 正确 // readOnlyList.add("d") // 错误! // 可以修改 mutableList.add("d") // 正确

4. Kotlin 实现单例的方式

  1. object 关键字(最简单):
kotlin
代码解读
复制代码
object Singleton { fun doSomething() {} }
  1. 伴生对象:
kotlin
代码解读
复制代码
class Singleton private constructor() { companion object { @Volatile private var instance: Singleton? = null fun getInstance(): Singleton = instance ?: synchronized(this) { instance ?: Singleton().also { instance = it } } } }

5. data 关键字

data 类自动生成:

  • equals()/hashCode()
  • toString()
  • componentN()
  • copy()
kotlin
代码解读
复制代码
data class User(val name: String, val age: Int)

6. 委托属性

委托属性允许将属性的 getter/setter 委托给另一个类。

常见使用场景:

  • 懒加载(lazy)
  • 观察者模式(Observable)
  • 存储属性(如 SharedPreferences)
kotlin
代码解读
复制代码
class Example { private val heavyObject by lazy { // 复杂初始化 HeavyObject() } }

7. with 与 apply 区别

kotlin
代码解读
复制代码
// with 返回最后一行结果 val result = with(person) { println(name) age + 1 } // apply 返回对象本身 val person = Person().apply { name = "张三" age = 20 }

8. 协程与线程的区别

协程比线程轻量的原因:

  1. 内存占用少:协程只需要几十字节的内存
  2. 上下文切换成本低:协程是用户态的切换
  3. 可以在单线程上运行多个协程

简单的流程图:

线程
系统级
重量级
内存占用大
协程
用户级
轻量级
内存占用小

9. infix 关键字

infix 允许以中缀表达式的形式调用函数。

kotlin
代码解读
复制代码
class Person { infix fun likes(thing: String) { println("I like $thing") } } // 使用方式: val person = Person() person likes "coding" // 等同于 person.likes("coding")

10. Kotlin 可见性修饰符

Kotlin 的可见性修饰符:

  • public(默认):所有地方可见
  • private:当前类内可见
  • protected:当前类及子类可见
  • internal:同一模块内可见

与 Java 的主要区别:

  • Kotlin 默认是 public
  • Kotlin 新增 internal
  • Kotlin 的外部类不能访问内部类的 private 成员

11. Kotlin 与 Java 混合开发注意事项

  1. 空安全处理:
kotlin
代码解读
复制代码
// Java 代码返回的类型在 Kotlin 中会被认为是可空的 fun handleJavaMethod(javaString: String?) { javaString?.length ?: 0 }
  1. 静态成员访问:
kotlin
代码解读
复制代码
// Java 静态方法在 Kotlin 中的调用 JavaClass.Companion.staticMethod()
  1. SAM 转换注意事项:
kotlin
代码解读
复制代码
// Java 接口 interface OnClickListener { void onClick(View view); } // Kotlin 调用 view.setOnClickListener { view -> // 处理点击 }

12. Kotlin 解构

解构允许将一个对象的多个属性同时赋值给多个变量:

kotlin
代码解读
复制代码
data class Person(val name: String, val age: Int) val person = Person("张三", 25) val (name, age) = person // 背后的原理是通过 componentN() 函数实现 val name = person.component1() val age = person.component2()

Kotlin 的解构声明(Destructuring Declarations)底层是通过 componentN() 函数实现的。让我详细解释:

  1. 基本使用:
kotlin
代码解读
复制代码
// 数据类自动支持解构 data class User(val name: String, val age: Int) fun example() { val user = User("Tom", 20) val (name, age) = user // 解构声明 // 底层实际调用 val name = user.component1() val age = user.component2() }
  1. 自定义解构:
kotlin
代码解读
复制代码
class Point(val x: Int, val y: Int) { // 手动实现 componentN 函数 operator fun component1() = x operator fun component2() = y } // 使用 val point = Point(10, 20) val (x, y) = point
  1. 常见应用场景:
kotlin
代码解读
复制代码
// 1. Map 遍历 val map = mapOf("a" to 1, "b" to 2) for ((key, value) in map) { println("$key = $value") } // 2. 函数返回多个值 data class Result(val success: Boolean, val data: String) fun getData(): Result { return Result(true, "数据") } val (success, data) = getData() // 3. Lambda 参数 map.forEach { (key, value) -> println("$key = $value") }
  1. 解构原理示例:
kotlin
代码解读
复制代码
// 编译前 data class Point(val x: Int, val y: Int) val (a, b) = Point(1, 2) // 编译后等价于 data class Point(val x: Int, val y: Int) { operator fun component1() = x operator fun component2() = y } val point = Point(1, 2) val a = point.component1() val b = point.component2()
  1. 实际应用:
kotlin
代码解读
复制代码
// 1. 处理网络响应 data class ApiResponse<T>( val code: Int, val message: String, val data: T? ) fun handleResponse() { val response = getApiResponse() val (code, message, data) = response // 处理响应 } // 2. 状态管理 data class UiState( val isLoading: Boolean, val data: List, val error: String? ) fun render(state: UiState) { val (isLoading, items, error) = state // 更新 UI }

总结:

  1. 解构的实现方式:
  • 通过 componentN() 函数
  • 数据类自动生成
  • 可手动实现
  1. 常用场景:
  • Map 遍历
  • 多返回值
  • 状态管理
  • 参数解构
  1. 注意事项:
  • 解构顺序要对应
  • 可以忽略部分值用 _
  • 注意性能开销
  1. 最佳实践:
  • 适度使用
  • 保持代码清晰
  • 考虑可读性

13. 内联函数

内联函数可以减少 Lambda 表达式的性能开销:

kotlin
代码解读
复制代码
inline fun measureTime(block: () -> Unit) { val start = System.currentTimeMillis() block() println("耗时:${System.currentTimeMillis() - start}ms") }

内联函数的工作原理图:

调用处代码
编译时
内联函数体复制到调用处
避免函数调用开销

14. Kotlin 构造方法

Kotlin 有主构造函数和次构造函数:

kotlin
代码解读
复制代码
class Person(val name: String) { // 主构造函数 var age: Int = 0 constructor(name: String, age: Int) : this(name) { // 次构造函数 this.age = age } init { // 初始化代码块 println("Creating person: $name") } }

15. Sequence

Sequence 是惰性求值的序列,对比即时求值的 List 更高效:

kotlin
代码解读
复制代码
// List 方式(即时求值) listOf(1, 2, 3, 4) .map { it * 2 } // 创建新列表 [2, 4, 6, 8] .filter { it > 5 } // 创建新列表 [6, 8] .first() // 返回 6 // Sequence 方式(惰性求值) sequenceOf(1, 2, 3, 4) .map { it * 2 } // 不立即执行 .filter { it > 5 } // 不立即执行 .first() // 只处理需要的元素

性能比较图:

List操作
创建多个中间集合
处理所有元素
Sequence操作
无中间集合
按需处理元素

16. 空安全处理

Kotlin 提供多种空安全处理机制:

kotlin
代码解读
复制代码
// 安全调用操作符 val length = str?.length // Elvis 操作符 val length = str?.length ?: 0 // 非空断言 val length = str!!.length // 智能转换 if (str != null) { println(str.length) // 编译器知道 str 非空 }

17. Any 与 Object

主要区别:

  • Any 是所有 Kotlin 类的超类
  • Any 只有 equals()、hashCode() 和 toString() 三个方法
  • Any 可以是空类型(Any?)
  • Object 是所有 Java 类的超类

18. 集合遍历方式

kotlin
代码解读
复制代码
val list = listOf(1, 2, 3) // 1. for 循环 for (item in list) { } // 2. forEach list.forEach { } // 3. 索引遍历 for (i in list.indices) { } // 4. withIndex for ((index, value) in list.withIndex()) { }

19. Kotlin 数据类型隐式转换

Kotlin 不支持数据类型的隐式转换,这是为了避免精度损失和运行时错误。

kotlin
代码解读
复制代码
val intNumber: Int = 100 // val longNumber: Long = intNumber // 编译错误 val longNumber: Long = intNumber.toLong() // 正确方式 // 数字类型转换方法 val number = 100 number.toByte() number.toShort() number.toInt() number.toLong() number.toFloat() number.toDouble()

转换关系图:

显式转换
不支持
源类型
目标类型
隐式转换
编译错误

20. 对象表达式与 Lambda 表达式的区别

kotlin
代码解读
复制代码
// 1. 对象表达式 val clickListener = object : View.OnClickListener { override fun onClick(v: View?) { // 处理点击 } } // 2. Lambda 表达式 val clickListener = { v: View? -> // 处理点击 }

主要区别:

  1. 内存分配:

    • 对象表达式会创建匿名类实例
    • Lambda 表达式通常会被编译器优化,不会创建额外对象
  2. 功能性:

    • 对象表达式可以实现多个接口
    • Lambda 表达式只能实现单个抽象方法

21. 协程的深入理解

协程的基本组件:

kotlin
代码解读
复制代码
// 1. 启动协程 GlobalScope.launch { // 协程代码 } // 2. 使用 suspend 函数 suspend fun fetchData(): Data { delay(1000) // 非阻塞延迟 return Data() } // 3. 协程作用域 class MyViewModel : ViewModel() { private val viewModelScope = CoroutineScope(Dispatchers.Main) fun loadData() { viewModelScope.launch { val data = fetchData() // 处理数据 } } }

协程的生命周期图:

协程创建
协程启动
运行中
挂起
完成
取消

22. 协程的上下文和调度器

kotlin
代码解读
复制代码
// 不同调度器的使用 coroutineScope { // UI 线程 launch(Dispatchers.Main) { updateUI() } // IO 操作 launch(Dispatchers.IO) { fetchData() } // CPU 密集型操作 launch(Dispatchers.Default) { processData() } }

调度器选择流程:

是
否
是
否
操作类型
是否UI操作?
Dispatchers.Main
是否IO操作?
Dispatchers.IO
Dispatchers.Default

23. 协程的异常处理

kotlin
代码解读
复制代码
// 1. try-catch 方式 launch { try { // 可能抛出异常的代码 } catch (e: Exception) { // 处理异常 } } // 2. 协程作用域的异常处理 val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } GlobalScope.launch(handler) { throw Exception("Error") } // 3. SupervisorJob 方式 val scope = CoroutineScope(SupervisorJob() + handler)

24. 协程与线程的详细对比

特性协程线程
内存占用~几十字节~1MB
切换成本用户态切换,非常轻量系统态切换,较重
并发量可同时运行数十万个受系统资源限制
调度控制完全由程序控制依赖操作系统调度
取消操作支持结构化取消难以安全取消

25. 协程的最佳实践

kotlin
代码解读
复制代码
class Repository { // 1. 使用适当的作用域 private val coroutineScope = CoroutineScope( Dispatchers.IO + SupervisorJob() ) // 2. 结构化并发 suspend fun fetchData() = coroutineScope { val part1 = async { fetchPart1() } val part2 = async { fetchPart2() } combineResults(part1.await(), part2.await()) } // 3. 优雅的异常处理 private val handler = CoroutineExceptionHandler { _, e -> // 处理异常 } }

26.Unit 与 Void 的区别

  1. 类型系统:

    • Unit 是一个实际的类,只有一个单例实例
    • Void 是一个不能实例化的类型
  2. 返回值:

kotlin
代码解读
复制代码
// Kotlin中的Unit fun doSomething(): Unit { // 不需要显式返回任何值 } // Java中的void public void doSomething() { // 不能返回值 }
  1. 泛型使用:
kotlin
代码解读
复制代码
// Kotlin中可以使用Unit作为泛型参数 interface Callback<T> { fun onSuccess(value: T) fun onFailure(error: Throwable) } // Unit用作泛型参数 val callback = object : Callback<Unit> { override fun onSuccess(value: Unit) { // 处理成功 } override fun onFailure(error: Throwable) { // 处理失败 } }
  1. 函数类型:
kotlin
代码解读
复制代码
// Unit在函数类型中的使用 val action: () -> Unit = { println("Hello") } // 等价的Java代码需要使用Void Runnable runnable = () -> System.out.println("Hello");

主要区别总结:

  • Unit 是一个具体的类型,有一个单例实例
  • Void 不能实例化
  • Unit 可以用在泛型中
  • Unit 在函数式编程中更加灵活
  • Kotlin 中的 Unit 相当于其他函数式编程语言中的 unit 或 void 类型
注:本文转载自juejin.cn的Nathan20240616的文章"https://juejin.cn/post/7473891059221053480"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

140
Android
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top