本文链接
核心思路:每个知识点都要和源码结合起来讲。
1、const val PI = 45 编译时常量不可以用于局部变量,为什么?
函数之内必须在运行时赋值,不符合编译时常量
2、编译时常量(compile-time constants)有什么用?
3、Kotlin基本类型也是引用类型,会不会耗费性能?不会,都转为基本类型了
4、range表达式
n in 0..10
n !in 0..10
5、函数默认public,其他修饰符
6、默认返回Unit可以不写,kt中Unit是单例类
7、Nothing类型是什么?
8、反引号是什么?
private fun `登录功能20230727环境测试功能`(name:String){
println(name)
}
9、kt中in和is是关键字,想要函数名为in,is怎么办?反引号
fun `in`(){}
`in`() // 调用
10、反引号可以用于函数加密,公司内部有文档
private fun `9867693746293234`(name:String){
// xxx
}
11、函数内联 - 有lambda作为参数就需要内联inline
fun main(args: Array<String>) {
login("wch"){
println(it)
}
}
// 此处lambda是一个参数,因此生成Function1的对象
// (String,String)-> Unit 会生成Function2
inline fun login(name:String, lambda:(String)->Unit){
lambda(name)
}
12、函数引用的例子
fun main(args: Array<String>) {
var method = ::login
method("wch"){
println(it)
}
}
inline fun login(name:String, lambda:(String)->Unit){
lambda(name)
}
13、函数可以作为返回值,有什么用?
fun calculateResult(): () -> Int {
val result = 10 // 假设这是一个复杂的计算过程
return { result }
}
val delayedResult = calculateResult()
val result = delayedResult() // 只有在需要时才执行计算
enum class SortOrder {
ASCENDING, DESCENDING
}
fun getSortComparator(order: SortOrder): (List<Int>) -> List<Int> {
return when (order) {
SortOrder.ASCENDING -> { list -> list.sorted() }
SortOrder.DESCENDING -> { list -> list.sortedDescending() }
}
}
val numbers = listOf(4, 2, 7, 1, 5)
val sortOrder = SortOrder.ASCENDING
val sortedNumbers = getSortComparator(sortOrder)(numbers) // 根据排序顺序返回不同的函数实现
fun performOperation(): () -> Unit {
return {
// 执行原始操作
println("Performing the original operation")
}
}
fun withLogging(originalFunction: () -> Unit): () -> Unit {
return {
println("Logging before operation")
originalFunction()
println("Logging after operation")
}
}
val operation = performOperation()
val decoratedOperation = withLogging(operation) // 包装原始函数,添加日志记录的功能
decoratedOperation() // 执行装饰后的操作,会在控制台输出相关日志
14、具名函数有什么用?
fun printResult(result:String){
println(result)
}
login("wch", ::printResult) //用符号引用调用具名函数
15、安全调用操作符
var name: String? = "feather"
name = null
name?.capitalize()
!!
确保是有数值的才能用,这个和java一样,有风险
16、let的安全调用
name?.let{
it.xxx() //不为null才会执行
}
17、非空断言、if判断
name!!.capitalize()
//or
if(name != null){
name.capitalize()
}
18、空合并 ?:
var name:String? = "李小龙"
name = null
println(name ?: "原来你是鼎鼎大名null")
// ?: 前面为空,才执行
// 如果name = null会执行 ?: 后面的
let + ?:
//let + ?:
var name:String? = "李小龙"
name = null
println(name?.let { "我的名字是$it" } ?: "原来我是鼎鼎大名null")
19、自定义异常
fun main(args: Array<String>) {
throw CustomException()
}
// 自定义异常
class CustomException : IllegalArgumentException("illegal")
checkNotNull(value)
requireNotNull(value)
var value = false
require(value) // false 会抛出异常
subString(0, index)
subString(0 until 10)
// 两者等价
val list = text.split(",") // 分割成List
println(list)
// C++ 和 Kt都有解构
val (v1, v2, v3, v4) = list
// 不接受第一个数据,反编译不会get(0),节省性能
val (_, v2, v3, v4) = list
在 Kotlin 中,解构(Destructuring)是一种将复合数据结构(如类、数据类、数组、集合等)的多个成员拆分为单个变量的技术。:
data class Point(val x: Int, val y: Int)
val point = Point(10, 20)
val (x, y) = point // 解构 Point 对象为两个独立的变量
println("x: $x, y: $y") // 输出 x: 10, y: 20
fun getUser(): Pair<String, Int> {
return Pair("John Doe", 25)
}
val (name, age) = getUser() // 解构函数返回的 Pair 对象
println("Name: $name, Age: $age") // 输出 Name: John Doe, Age: 25
val numbers = arrayOf(1, 2, 3, 4, 5)
val (first, second, *rest) = numbers // 解构数组元素为单独的变量
println("First: $first, Second: $second, Rest: $rest") // 输出 First: 1, Second: 2, Rest: [3, 4, 5]
val list = listOf("apple", "banana", "cherry")
val (fruit1, fruit2) = list // 解构列表元素为单独的变量
println("Fruit 1: $fruit1, Fruit 2: $fruit2") // 输出 Fruit 1: apple, Fruit 2: banana
val person = mapOf("name" to "John", "age" to 30)
person.forEach { (key, value) ->
println("Key: $key, Value: $value")
}
data class Product(val name: String, val price: Double)
val products = listOf(
Product("Apple", 1.99),
Product("Banana", 0.99),
Product("Cherry", 2.49)
)
val (expensiveProducts, cheapProducts) = products.partition { (_, price) -> price > 1.0 }
println("Expensive Products: $expensiveProducts")
println("Cheap Products: $cheapProducts")
data class Person(val name: String, val age: Int, val address: Address)
data class Address(val street: String, val city: String)
val person = Person("John Doe", 30, Address("123 Main St", "City"))
val (name, _, (street, city)) = person
println("Name: $name")
println("Street: $street, City: $city")
partition
是用于将集合元素拆分为满足某个条件和不满足该条件的两个集合的函数。它返回一个包含两个集合的 Pair
对象,其中一个集合包含满足条件的元素,另一个集合包含不满足条件的元素。
val numbers = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val (evenNumbers, oddNumbers) = numbers.partition { it % 2 == 0 }
println("Even Numbers: $evenNumbers")
println("Odd Numbers: $oddNumbers")
输出结果为:
Even Numbers: [2, 4, 6, 8, 10]
Odd Numbers: [1, 3, 5, 7, 9]
val r1 = sourcePwd.replace(Regex("[AKMNO]")){ //Regex 中 写正则表达式
it.value // 啥也没做
// 可以加逻辑,把AKMNO替换成需要的内容
}
// 用正则表达式Regex进行替换,可以加密和解密
val newName = name.replace(Regex("[ADG]")){
when(it.value){
"A"->"1"
"D"->"2"
"G"->"3"
else->it.value
}
}
== 内容的比较,等价于 equals
=== 引用的比较
val name1 = "Derry"
val name2 = "Derry"
name1 === name2
结果:
true // 字符串常量池同一个,是true
val name1 = "Derry"
val name2 = "derry".capitalize() // "Derry"
name1 === name2
结果:
false // name2会生成另一个Derry,和常量池原先的Derry是两个对象
val number:Int = "666".toInt()
val number:Int = "666.6f".toInt() // 会报错
val number:Int? = "666.6f".toIntOrNull() // 不会报错为Null
64.55.toInt() // 四舍五入
64.55.roundToInt() // 四舍五入
// 保留小数点
"%.3f".format(65.742355) // %是格式化字符串的标志,%.3f小数点后三位
"feather".apply{
// 直接this拿到 feather,可以直接调用 String的方法
toLowerCase()
this[length - 1]
}
适合链式调用: 例如文件解析
file.apply{
setExecutable(true)
}.apply{
setReadable(true)
}.apply{
// xxx
}
apply设置对象的成员变量,run也可以这么做(都有this)
class Configuration {
var host: String = ""
var port: Int = 0
var timeout: Int = 0
}
val config = Configuration().apply {
host = "example.com"
port = 8080
timeout = 5000
}
apply源码:T.() 拥有this
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this // return this,用于链式调用
}
let源码: (T) 拥有it
public inline fun <T, R> T.let(block: (T) -> R): R {
return block(this)
}
使用:
val r = a?.let {
// 能执行到这里it一定不为null
if(it.isBlank()){
"DEFAULT"
}else{
it
}
}
info.run{ // this
"hello" // 最后一行返回
}
run的源码: T.() 拥有this
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
return block()
}
with和run的使用方法不同,其他一模一样
with("wch"){
}
// T.() 拥有this
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
also源码:(T) 拥有it
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
true时,返回对象本身,否则返回null
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
return if (predicate(this)) this else null
}
false时,返回对象本身,否则返回null
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
return if (!predicate(this)) this else null
}
let = block(this) // it
apply block() = this // this // 返回自身,链式
also block(this) = this // it // 返回自身,链式
with = receiver(this) // this
run = block() // this
takeIf = precidate(this) // it
takeUnless = !precidate(this) // it
list的注意点
list.getOrElse(index){"数组越界了"}
list.getOrNull(index)
为什么是不可变的?==> 协变
List
是生产者,只能取数值。
val list = mutableListOf(1, 2, 3, 4, 5)
list.toList() // 转为不可变集合
val list = listOf(1, 2, 3, 4, 5)
list.toMutableList() // 转为可变集合
list += 99 // 运算符重载,+= -=
list.removeIf{ true } // 自动遍历全部移除
list.removeIf { it%2 == 0 } // 移除符合条件的数据
for(i in list){
//111
}
list.forEach{
//222
}
// 第三种方式
list.forEachIndexed{ index: Int, item: Int ->
print("$index $item,")
}
val set = setOf("a", "cd", "xsw")
set.elementAt(3) // 可能越界
set.elementAtOrElse(3, {"Set越界咯"})
set.elementAtOrNull(3)
// 配合 空合并
println(set.elementAtOrNull(3) ?: "为null")
// 可变Set
val set = mutableSetOf<String>("a", "cd", "xsw")
val list = mutableSetOf<String>("a", "cd", "xsw")
list.toSet() // 去重
list.toSet().toList() // 去重
list.distinct() // 快捷去重,内部先转为Set再转为List
数组的创建
创建、读取、遍历
// 创建Map,两者等价
val map = mapOf("Derry" to (13.4), "wch" to (38.9)) // 前者key,后者value
val map2 = mapOf(Pair("wch", 20), Pair("feather", 37)) // 源码中
// 读取Map,五中方式
map["wch"] // kt方式,返回null
map.get("xwd") // java方式,返回null
map.getOrDefault("feather", 37.8) // 默认值保护
map.getOrElse("khw") { 98.2 }
map.getValue("zas") // 会崩溃!!!不要用
// 遍历
// 第一种
map.forEach{ // it = Entry
it.key
it.value
}
// 第二种
map.forEach{ key,value -> println("$key $value") }
// 第三种: 解构
map.forEach{ (k,v) -> println("$k $v") }
// 第四种:in
for(item in map){
}
可变Map: getOrPut
// 存在取出,不存在存入
val map = mutableMapOf("Derry" to (13.4), "wch" to (38.9)) // 前者key,后者value
map.getOrPut("fff") { 10.9 }
1、类中的字段默认的访问权限修饰符是什么?为什么外界可以访问?
class MyKt{
var name = "wch"
get() = field // 默认有get
set(value) { // 默认有set
field = value
}
}
// 在使用时随机获取了值
val name:Int // field已经失效了 // 没有name这个成员变量了
get() = (1..1000).shuffled().first() // 随机排序后,取第一个数据
// int getName(){return (1..1000).shuffled().first()}
var name:String ? = null
class MyKt(){ // MyKt()主构造函数,可以不写
}
// _name _age 都是临时输入类型,不可以直接使用
class MyKt(_name:String, _age:Int){
}
// 使用输入数据一
class MyKt(_name:String, _age:Int){
val name = _name
// 可以额外修改
get() = "My name is $_name"
}
// 使用输入数据二
class MyKt(val name:String, _age:Int){
}
// 次构造,必须调用主构造
constructor(name: String, sex: Char):this(name){
}
1、所有的构造函数都是用了默认参数,调用MyKt()应该是哪个构造函数?
class MyKt{
lateinit var name:String
fun initName(){
name = "feather"
}
fun getNameUnsafe(){
if(name != null){
println(name)
}
}
fun getNameSage(){
if(::name.isInitialized){
println(name)
}else{
println("你还没有初始化哦")
}
}
}
class MyKt{
// 普通方式,非懒加载
val name:String = requestName()
// 懒加载
val name2 by lazy { requestName() }
fun requestName() = "feather"
}
1、init代码块前后具有顺序
// Error!
init{
name = "feather"
}
var name:String
class MyKt{
val name:String
init{
getNameInfo()
name = "feather"
}
fun getNameInfo() {
println("${name[0]}")
}
}
fun main() {
MyKt().getNameInfo()
}
// 1. 主构造函数
class MyKt(_name:String){
// 2. info = name = null // 一定拿到null
val info = getNameInfo()
// 3. name才赋值
val name = _name
fun getNameInfo() = name
}
fun main() {
MyKt("feather").info.length
}
open class Peron(open val name:String){
open fun printName(){}
}
class Student(override val name:String):Peron(name){
override fun printName(){}
}
if(p is Student){
p as Student
}
val p:Person = Student("wch")
// is后自动转换,可以直接调用
if(p is Student)
{
p.printStudentName()
}
1、Any是什么?相当于Object
1、object是什么有什么用?
object MyKt{
init {
println("init初始化")
}
}
// 单例类的内部默认生成了INSTANCE,调用show()等价于
// MyKt.INSTANCE.show()
MyKt.show()
MyKt生成的代码如下:
// final类
// INSTANCE在static时初始化,
public final class MyKt {
@NotNull
public static final MyKt INSTANCE;
public final void show() {}
private MyKt() {}
static {
MyKt var0 = new MyKt();
INSTANCE = var0; // 唯一
String var1 = "init初始化";
System.out.println(var1);
}
}
// 具名对象,接收匿名对象
val obj = object : OnDialogClickListener {
override fun onCancelClick() {
TODO("Not yet implemented")
}
override fun onConfimrClick() {
TODO("Not yet implemented")
}
}
// Runnable
val obj = object : Runnable {
override fun run() {
TODO("Not yet implemented")
}
}
// lambda方式 - 一个方法的函数式接口
val obj = Runnable {
println("lambda方式")
}
1、为什么会有伴生对象?
class MyKt{
companion object{
const val info = "info"
}
}
MyKt.info
1、Kt中默认内部类不可以访问外部类,需要增加修饰符 => 默认是嵌套类
class MyKt{
inner class InnerClass{}
}
1、数据类的定义,和普通类的区别是什么?
// 普通类 只要get、set
class ResponseBean(var code:Int, var msg:String, var data:String)
// 数据类
data class ResponseBean2(var code:Int, var msg:String, var data:String)
2、数据类使用场景和注意点?
自定义解构功能:
// 普通类
class ResponseBean(var code:Int, var msg:String, var data:String){
operator fun component1() = code
operator fun component2() = msg
operator fun component3() = data
// xxx
}
operator进行重载
data class Data(var number1:Int, var number2:Int){
operator fun plus(data: Data): Data {
number1 += data.number1
number2 += data.number2
return this
}
}
fun main() {
// C++中重载 + - 就可以了
// Kt中需要关键字 plus
val result = Data(1, 1) + Data(2, 2)
println(result)
}
查看Kt支持的所有运算符:点号,IDE会有提示
operator fun Data.xx()
1、Kt中枚举类为什么和Java不同还有个class?
// 1、基本的枚举类
enum class Week{
星期一,
星期二,
星期三,
星期四,
星期五,
星期六,
星期日
}
fun main() {
println(Week.星期一)
println(Week.星期日)
// 枚举的值 等价于 枚举本身
println(Week.星期四 is Week)
}
2、枚举类中传入对象+定义方法
// 枚举类中存储对象
class Weather(val info:String)
enum class Week(var weather: Weather){
星期一(Weather("晴朗")),
星期二(Weather("小雨")),
星期三(Weather("中雨")),
星期四(Weather("暴雨")),
星期五(Weather("阴")),
星期六(Weather("雨夹雪")),
星期日(Weather("大雪"));
// 枚举中定义方法
fun show() = println("$name:${weather.info}")
// 更新属性值
fun updateWeather(weather: Weather){
this.weather = weather
}
}
fun main() {
println(Week.星期日)
println(Week.星期一.weather.info)
Week.星期四.show()
Week.星期四.updateWeather(Weather("大雪"))
Week.星期四.show()
}
3、枚举类中定义抽象方法
enum class Week {
星期一 {
override fun printInfo() {
println(this.name)
}
},
星期二 {
override fun printInfo() {
println(this.name)
}
};
abstract fun printInfo()
}
fun main() {
var day = Week.星期一
day.printInfo()
day = Week.星期二
day.printInfo()
}
什么是代数数据类型?
when配合枚举,数据非常明确,不需要else
1、密封类是什么?
sealed class Result {
class Success(val data: String) : Result()
class Error(val message: String) : Result()
}
// 密封类的子类进行模式匹配,使用 `when` 表达式来处理不同的子类情况
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println("Success: ${result.data}")
is Result.Error -> println("Error: ${result.message}")
}
}
2、密封类的使用例子
sealed class NetworkResult {
class Success(val data: String) : NetworkResult()
class Error(val message: String) : NetworkResult()
object Loading : NetworkResult()
}
fun handleNetworkResponse(result: NetworkResult) {
when (result) {
is NetworkResult.Success -> println("成功: ${result.data}")
is NetworkResult.Error -> println("错误: ${result.message}")
NetworkResult.Loading -> println("加载中...")
}
}
sealed class Color {
object Red : Color()
object Green : Color()
object Blue : Color()
class Custom(val hexCode: String) : Color()
}
fun printColor(color: Color) {
when (color) {
Color.Red -> println("红色")
Color.Green -> println("绿色")
Color.Blue -> println("蓝色")
is Color.Custom -> println("自定义颜色: ${color.hexCode}")
}
}
3、密封类中object和class的区别
abstract class 抽象类
class KtClass<T>{
xxx
}
// 动态参数的泛型,可以传入null
class MyKt<T>(vararg objects: T) {
// 用于输出的T,生产者,只读。---规定。
val objectArray: Array<out T> = objects
}
为什么需要协变和逆变?
默认,List和List是两个类型
// out 生产者,只可以作为返回值
interface Producer<out T>{
fun producer() : T
// fun comsume(item : T)
}
// out
// List = List
// in 生产者,可以修改值,但不可以作为方法返回出去
interface Comsumer<in T>{
fun comsume(item : T)
// fun producer() : T
}
// List = List
类似Java中,List和List是两个类型
什么时候需要out 和 in?
1、reified的作用是什么?
reified
关键字用于在具有inline
函数修饰符的函数中获取“泛型参数的实际类型”。以下是使用reified
的基本语法:
inline fun <reified T> functionName() {
// 在函数体中可以使用 T 的实际类型
T::class.java
}
2、要使用reified
关键字,需要注意以下几点:
reified
只能在具有inline
修饰符的函数中使用,因为它需要在编译时对函数的字节码进行改变,并保留类型信息。reified
只能用于泛型类型参数,即在尖括号< >
中的泛型参数前面使用reified
关键字。reified
的函数体内,可以直接使用泛型类型参数T
的实际类型,如T::class
、T::class.java
等。3、让is和T配合使用
class Person(val name: String)
class Producer{
inline fun<reified T> test(obj: Person){
obj.takeIf { it is T } as T
}
}
扩展方法:增强类、第三方库等类
class MyKt(val name:String)
fun MyKt.showName(){
println("name:$name")
}
// 扩展函数生成的代码
// 调用时会自动把对象传入该方法,进行调用
public static final void show(MyKt $this$show){
System.out.println("name:" + $this$show.getName());
}
扩展同名函数会覆盖原先的函数,子类(Int)的扩展函数优先于父类(Any)的扩展函数
1、对Any类扩展会导致所有类都有该方法
let源码:
@kotlin.internal.InlineOnly // 注解主要用于标记 Kotlin 内部的函数,以指示编译器将函数的实现内联到调用点
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
// 最后一行
public inline fun <T, R> T.let(block: (T) -> R): R { // it,最后一行作为返回值
return block(this)
}
public inline fun <T,R> with(receiver:T, block: T.() -> R):R{ // this,最后一行
return receiver.block()
}
// 自身
public inline fun <T> T.apply(block: T.() -> Unit): T { // this,自身(链式调用)
block()
return this
}
public inline fun <T> T.also(block: (T) -> Unit): T{ // it,自身(链式调用)
block(this)
return this
}
// it,自身或者null
public inline fun <T> T.takeIf(predicate: (T)->Boolean): T?{ // it,自身或者null
return if(predicate(this)) this else null
}
public inline fun <T> T.takeUnless(predicate: (T)->Boolean): T?{ // it,自身或者null
return if(!predicate(this)) this else null
}
扩展属性:val+get()只读不可以修改
val String.info
get() = "MyInfo"
生成的Java代码:
@NotNull
public static final String getInfo(@NotNull String $this$info) {
Intrinsics.checkNotNullParameter($this$info, "$this$info");
return "MyInfo";
}
infix是什么?有什么好处?
"wch" to 10
的效果,而不需要"wch".to(10)
val map = mapOf("wch" to 10, "cyz" to 20)
println(map)
// 需要成为A的扩展函数
public infix fun<A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
扩展文件:将相关扩展函数写到一个文件中,需要时引入文件。
将方法名重命名:缩减代码量
import kotlin.let as l
fun main() {
"feather".l {
println("it = $it")
}
}
// 第二个例子:短了很多
import com.personal.tax.compose.IWantToDoMyLicense as p
fun IWantToDoMyLicense(){
println("Hello World!")
}
fun main() {
p()
}
List
val list = listOf("wch", "cyz", "xxx")
println(list)
// 经过变化后,最后一行,作为新元素加入新集合
var list2 = list.map {
when(it){
"wch" -> 1
"cyz" -> 2
"xxx" -> 3
else -> -1
}
}
println(list2)
flatMap
用于对集合进行转换和扁平化操作。List
,最终结果 List>
fun main() {
val numbers = listOf(listOf(1, 2), listOf(3, 4), listOf(5, 6))
// 使用flatMap将多个列表扁平化为一个列表
val flattenedNumbers = numbers.flatMap { it }
println(flattenedNumbers) // 输出: [1, 2, 3, 4, 5, 6]
// 使用flatMap进行转换和扁平化操作
val squaredNumbers = numbers.flatMap { it.map { number -> number * number } }
println(squaredNumbers) // 输出: [1, 4, 9, 16, 25, 36]
}
符合条件的添加到新集合中(true)
// 过滤掉int类型的list元素
val list = listOf(listOf(1,2,3), listOf("a","b","c"), listOf(1.1, 2.2, 3.3))
println(list) // [[1, 2, 3], [a, b, c], [1.1, 2.2, 3.3]]
val list2 = list.filter {
it.getOrNull(0) !is Int // 将Int集合全部去除
}
println(list2) // [[a, b, c], [1.1, 2.2, 3.3]]
// 全部扁平化展开后,去除Int
val list3 = list.flatMap { sublist->
sublist.filter { item->
item !is Int
}
}
println(list3) // [a, b, c, 1.1, 2.2, 3.3]
合并集合
// 以Pair形式合并元素
val list = listOf(1,2,3)
val list2 = listOf("a","b","c")
var zipList = list.zip(list2)
println(zipList)
//输出结果:[(1, a), (2, b), (3, c)]
Kotlin调用Java要注意为kong陷阱
// Java类
public class Person {
String mName;
public Person(){
}
public Person(String name){
mName = name;
}
}
错误交互:空指针
// Kt和Java错误交互
// name类型为 String! 可能为空
val name = Person("wch").mName
val name2 = Person().mName
println(name.length)
println(name2.length)
正确交互:规则一 小心使用
val name = Person("wch").mName
val name2 = Person().mName
println(name?.length)
println(name2?.length)
正确交互:规则二 强制判断
val name:String? = Person("wch").mName
val name2:String? = Person().mName
println(name?.length)
println(name2?.length)
改变Kotlin生成类的名称,方便Java调用,需要在kt文件最开头
@file:JvmName("Stu")
Java可以直接访问Kt字段,不再需要getXXX()
Java可以直接访问Kt Companion中静态字段和静态方法,而不需要MyKt.Companion.xxx
Kt中默认参数,需要生成对应于Java的多个重载方法。
饿汉式
object Singleton0
懒汉式(安全)
class Singleton private constructor(){
companion object{
// 底层采用双重检查加锁
val instance:Singleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED){Singleton()}
}
}
mode属性:
LazyThreadSafetyMode.NONE
: 这是默认的模式,懒加载属性是非线程安全的。LazyThreadSafetyMode.SYNCHRONIZED
: 懒加载属性是线程安全的。在多线程环境中,by lazy
会使用 synchronized
来确保只有一个线程可以初始化属性。这会导致多个线程在访问该属性时进行阻塞等待。LazyThreadSafetyMode.PUBLICATION
: 懒加载属性是线程安全的。在多线程环境中,by lazy
使用一种更高效的并发控制来进行初始化,可以在多个线程同时访问属性时进行初始化。这种模式通常比 SYNCHRONIZED
模式具有更好的性能。LazyThreadSafetyMode.PUBLICATION不适合单例模式,可能创建更多的实例