kotlin面试题_码农乐园的博客-CSDN博客_kotlin面试题
Kotlin协程是个什么东西? - 掘金
就像 Java 一样,Kotlin 代码也被编译成 Java 字节码,并在运行时由 Java 虚拟机即 JVM 执行。当一个名为 Kotlin 的文件Main.kt被编译后,它最终会变成一个类,然后生成该类的字节码。字节码文件的名称将是MainKt.class,并且该文件将由 JVM 执行。
声明的变量本质val上const都是不可变的。但是const变量的值必须在编译时知道,而val变量的值也可以在运行时分配。
安全调用运算符 ie?.用于检查变量的值是否为空。如果为 null 则返回 null ,否则返回所需的值。
var name: String? = "码农乐园"
println(name?.length) // 8
name = null
println(name?.length) // null
如果要在变量值为 null 时抛出 NullPointerException,则可以使用 null 检查或!!运算符。
var name: String? = "码农乐园"
println(name?.length) // 8
name = null
println(name!!.length) // KotlinNullPointerException
Kotlin 中没有三元运算符,但可以通过 if-else 或 Elvis 运算符来使用三元运算符的功能。
在 Kotlin 中,您可以使用 null 安全属性将 null 值分配给变量。要检查一个值是否具有空值,那么您可以使用 if-else 或可以使用 Elvis 运算符?:,例如:
var name:String? = "码农乐园"
val nameLength = name?.length ?: -1
println(nameLength)
上面使用的 Elvis 运算符(?:)将返回 name 的长度,如果 value 不为 null,否则如果 value 为 null,则返回-1。
将 Kotlin 源文件转换为 Java 源文件的步骤:
在 IntelliJ IDEA / Android Studio 中打开您的 Kotlin 项目。
然后导航到工具 > Kotlin > 显示 Kotlin 字节码。
现在单击反编译按钮以从字节码中获取您的 Java 代码。
@JvmStatic:这个注解用来告诉编译器该方法是静态方法,可以在Java代码中使用。
@JvmOverloads:要在 Java 代码中使用 Kotlin 代码中作为参数传递的默认值,我们需要使用@JvmOverloads注解。
@JvmField:要在不使用任何 getter 和 setter 的情况下从 Java 代码访问 Kotlin 类的字段,我们需要@JvmField在 Kotlin 代码中使用。
lateinit是后期初始化。
通常,声明为非空类型的属性必须在构造函数中初始化。然而,这通常并不方便。
例如,可以通过依赖注入来初始化属性,或者在单元测试的 setup 方法中初始化属性。在这种情况下,您不能在构造函数中提供非 null 初始化程序,但您仍然希望在引用类主体内的属性时避免 null 检查。要处理这种情况,您可以使用 lateinit 修饰符标记该属性。
您可以在方法的帮助下使用它之前检查 lateinit 变量是否已初始化isInitialized。如果 lateinit 属性已初始化,则此方法将返回 true,否则将返回 false。例如:
class Person {
lateinit var name: String
fun initializeName() {
println(this::name.isInitialized)
name = "MindOrks" // initializing name
println(this::name.isInitialized)
}
}
fun main(args: Array
Person().initializeName()
}
lazy 只能用于 val 属性,而 lateinit 只能用于 var,因为它不能编译为 final 字段,因此不能保证不变性。
如果您希望您的属性以一种事先可能未知的方式从外部初始化,请使用 lateinit。
主构造函数
主构造函数没有函数体,直接定义在类名后。每个类都会默认带一个不带参数的构造函数,也可以直接定义参数,如果需要在构造函数中进行初始化工作,可以用init关键字:
class Student {}
class Student(var name: String) {
init { Log.e(TAG,"name=$name") }
}
class Student constructor(var name: String) {
init { Log.e(TAG,"name=$name") }
}
次构造函数
除了类名后这种主构造函数,其他的构造函数方法就是通过constructor关键字来定义次构造函数,一个类可以定义多个次构造函数。如果主构造函数和次构造函数同时存在的时候,次构造函数必须调用主构造函数。
class Student{
private val username: String
constructor(username: String){ this.username = username }
}
class Student(username: String) {
private var username: String
private var age: Int
init { this.username = username this.age = 10 }
constructor(username: String, age: Int) : this(username) { this.age = age }
}
简单举个例子,具体的说明大家可以翻翻以前的文章——协程三问。
GlobalScope.launch(Dispatchers.Main) {
var name = ioTask()
updateUI(name)
var name1 = ioTask()
updateUI(name1)
var name2 = ioTask()
updateUI(name2)
}
private suspend fun ioTask(): String {
var name = ""
withContext(Dispatchers.IO) { //耗时操作,比如网络接口访问 name = "jimu" }
return name
}
GlobalScope.launch 去开启一个协程
Dispatchers.Main 表示运行在主线程
suspend 关键字用于标记挂起函数的关键字
withContext 函数用来构建一个协程作用域,可以标明作用线程,比如这里的Dispatchers.IO。这个函数必须在挂起函数或者协程中执行