以一个java老鸟的角度,如何去看 kotlin。 Java源代码应该如何用Kotlin重构。 如何正确学习kotlin并且应用到实际开发中。本文将会探究。
本文分两大块,重难点和潜规则。
重难点:Kotlin中可以独立出来讲解的大块知识点。提供单独Demo。这部分大多数是Kotlin开创的新概念(相比于Java)。
潜规则:Kotlin是谷歌用来替换Java的,它和java百分百完全兼容,但是实际上java转成kotlin之后,需要我们手动修改很多东西,甚至某些部分必须打散重构来达到最优编码。其中,kotlin的某些特性和java不同,甚至完全反转。这部分知识点比较零碎,单独Demo不方便提供,就以小例子的形式来写。
重难点
潜规则
Kotlin 文件和类不存在一对一关系
共生体
继承
修饰符
空指针问题
想了很久,关于协程的内容,在官网上确实有很多内容,基础知识概念,基本使用,以及 流操作,通道,异常处理,并发处理等,不方便在这里展开。具体的参照:Kotlin中文网
这里具体去学习。本章节,只总结一下近期查阅资料并经本人验证的知识点。
英文 coroutines : /,kəuru:'ti:n/ 意: 协同程序。 简称协程。
Kotlin提出协程概念,是为了简化异步编程,让开发者更容易控制函数的执行流程。
在操作系统OS中,进程是资源分配的最小单位,线程是任务调度的最小单位。而协程则是处在线程内部的**“微线程”,或者说轻量级线程**。 由于线程在OS中是稀缺资源,所有OS平台的线程数量都是有上限的,平时编程我们会用线程池来管理线程数量,熟悉线程池的同学应该知道,线程池管理线程,无论是核心线程还是非核心线程,都不会随意去创建,除非迫不得已。
用线程解决异步问题
用协程解决异步问题
module级别的build.gradle 中 引入库依赖,推荐使用最新版(目前稳定版是1.3.3)
dependencies { //... implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3" // 如果你需要用到协程调度器Dispatchers的话,必须加上这个依赖 }
创建协程有多种方式,全局/独立,同步/异步,并且可以指定 “协程调度器”
runBlocking
fun main() { runBlocking { println("这是runBlocking协程...") } }
launch
fun main() { runBlocking { println("这是runBlocking协程...") launch { println("这是runBlocking内部的runBlocking协程...") delay(2000) println("延迟了2000MS之后,再打印") } } }
GlobalScope.launch
fun main() { println("协程,相对于主线程来说,都是异步的,也就是说,你在这里插入协程逻辑,主线程的逻辑并不会被阻塞") GlobalScope.launch { delay(3000) println("GlobalScope.launch 创建协程 ") } println("主线程继续") Thread.sleep(5000) }
Global.async
fun main() { runBlocking { val async: Deferred<String> = GlobalScope.async { println("这是一个异步协程,他将返回一个Deferred") delay(2000) "异步任务返回值" } println("主线程继续:" + async.await()) } Thread.sleep(5000) }
关心协程的人一般都会十分关注它到底能给我们异步编程带来怎样的便利。这里总结几个不用协程实现起来很麻烦的骚操作。
如果有一个函数,它的返回值需要等到多个耗时的异步任务都执行完毕返回之后,组合所有任务的返回值作为 最终返回值
fun test6(): String = runBlocking { var finalRes = "" coroutineScope { launch { delay(1000) finalRes = finalRes.plus("1") } launch { delay(2000) finalRes = finalRes.plus("2") } launch { delay(3000) finalRes = finalRes.plus("3") } } finalRes } fun main() { val test6 = test6() println("最终返回值是: $test6") }
最终返回结果为(延迟3秒之后打印):
最终返回值是: 123
如果有一个函数,需要顺序执行多个网络请求,并且后一个请求依赖前一个请求的执行结果
import kotlinx.coroutines.* suspend fun getToken(): String { for (i in 0..10) { println("异步请求正在执行:getToken :$i") delay(100) } return "ask" } suspend fun getResponse(token: String): String { for (i in 0..10) { println("异步请求正在执行:getResponse :$token $i") delay(100) } return "response" } fun setText(response: String) { println("setText 执行,时间: ${System.currentTimeMillis()}") } fun main() { GlobalScope.launch(Dispatchers.Unconfined) { var token = GlobalScope.async(Dispatchers.Unconfined) { return@async getToken() }.await() // 创建异步任务,并且 阻塞执行 await 是阻塞执行取得结果 var response = GlobalScope.async(Dispatchers.Unconfined) { return@async getResponse(token) }.await() // 创建异步任务,并且立即执行 setText(response) } Thread.sleep(20000) }
执行结果:
异步请求正在执行:getToken :0 异步请求正在执行:getToken :1 异步请求正在执行:getToken :2 异步请求正在执行:getToken :3 异步请求正在执行:getToken :4 异步请求正在执行:getToken :5 异步请求正在执行:getToken :6 异步请求正在执行:getToken :7 异步请求正在执行:getToken :8 异步请求正在执行:getToken :9 异步请求正在执行:getToken :10 异步请求正在执行:getResponse :ask 0 异步请求正在执行:getResponse :ask 1 异步请求正在执行:getResponse :ask 2 异步请求正在执行:getResponse :ask 3 异步请求正在执行:getResponse :ask 4 异步请求正在执行:getResponse :ask 5 异步请求正在执行:getResponse :ask 6 异步请求正在执行:getResponse :ask 7 异步请求正在执行:getResponse :ask 8 异步请求正在执行:getResponse :ask 9 异步请求正在执行:getResponse :ask 10 setText 执行,时间: 1578904290520
当前正在执行一项异步任务,但是你突然不想要它执行了,随时可以取消
fun main() { // 协程任务 val job = GlobalScope.launch(Dispatchers.IO) { for (i in 0..100){// 每次挂起100MS,100次也就是10秒 println("协程正在执行 $i") delay(100) } } // 但是我在1秒之后就取消协程 Thread.sleep(1000) job?.cancel() println( "btn_right 结束协程") }
执行结果(本该执行100轮的打印,只持续了10轮):
协程正在执行 0 协程正在执行 1 协程正在执行 2 协程正在执行 3 协程正在执行 4 协程正在执行 5 协程正在执行 6 协程正在执行 7 协程正在执行 8 协程正在执行 9 btn_right 结束协程 Process finished with exit code 0
如果你想让一个任务最多执行3秒,超过3秒则自动取消
import kotlinx.coroutines.* fun main() = runBlocking { println("限时任务中结果是:" + getResFromTimeoutTask()) } suspend fun getResFromTimeoutTask(): String? { // 忘了,它会保证内部的协程代码都执行完毕,所以不能这么写 return withTimeoutOrNull(1300) { for (i in 0..10) { println("I'm sleeping $i ...") delay(500) } "执行结束" } }
执行结果
I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... 限时任务中结果是:null Process finished with exit code 0
协程作为kotlin 区别于java的新概念,它的出现是为了解决java不好解决的问题,比如层层回调导致代码臃肿,比如 异步任务执行流程不好操控等。本章节篇幅有限,无法展开说明,但是对于新手而言,看完本章应该能对协程的作用有一个大概的认知。本人也是初步研究,后续有更深入的了解之后,再进行专文讲解吧。
说人话,像是一元操作符 ++自加,二元操作符 +相加 ,默认只支持数字类型,比如Int. 但是通过操作符的重载,我们可以让任意类 都能 ++自加,且返回一个想要的对象。操作符执行的逻辑,完全看我们如何去设计。
按元素级别
一元
表达式 | 对应函数 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a++ | a.inc() |
a– | a.dec() |
二元
表达式 | 对应函数 |
---|---|
a+b | a.plus(b) |
a-b | a.minus(b) |
a*b | a.times(b) |
a/b | a.div(b) |
a%b | a.rem(b) |
a…b | a.range(b) |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
a[i] | a.get(i) |
a[i,j] | a.get(i,j) |
a[i_1,…,i_n] | a.get(i_1,…,i_n) |
a[i]=b | a.set(i,b) |
a[i,j]=b | a.set(i,j,b) |
a[i_1,…,i_n]=b | a.set(i_1,…,i_j,b) |
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i,j) | a.invoke(i,j) |
a(i_1,…,i_n) | a.invoke(i_1,…,i_n) |
a+=b | a.plusAssign(b) |
a-=b | a.minusAssign(b) |
a*=b | a.timesAssign(b) |
a/=b | a.divAssign(b) |
a%=b | a.modAssign(b) |
a > b | a.compareTo(b)>0 |
a < b | a.compareTo(b)<0 |
a>=b | a.compareTo(b)>=0 |
a<=b | a.compareTo(b)<=0 |
按实现方式
成员函数
扩展函数
看到上面的一大堆,肯定有点懵,看个例子解决疑问。上面我用两种维度来对操作符重载进行了分类,那么,先试试:成员函数的方式来重载一个一元操作符
class A(i: Int, j: Int) { var i: Int = i var j: Int = j /** * 重载++操作 */ operator fun inc(): A { return A(i++, j++) } override fun toString(): String { return "[i=$i , j=$j]" } }
如上代码,注意看:
operator fun inc(): A { return A(i++, j++) }
Kotlin的操作符重载和 c++,dart语言内的操作符重载写法完全不同,它不再是直接把操作符放到了 重写的过程中,而是每一种支持重载的操作符都有一个对应的 函数名
正如:上表格中的 a++ 操作符,对应的函数就是 a.inc()
调用的时候:
fun main() { var a = A(1, 2) println("a:$a") println("a++:${a++}") }
打印结果:
a:[i=1 , j=2] a++:[i=2 , j=3]
再看一个二元运算符重载的栗子,这次我们不用成员函数,而是用扩展函数:
class A(i: Int, j: Int) { var i: Int = i var j: Int = j override fun toString(): String { return "[i=$i , j=$j]" } } /** * 重载A类的 x+y 操作 */ operator fun A.plus(a: A): A { return A(this.i + a.i, this.j + a.j) } fun main() { val x = A(1,1) val y = A(2,2) println(x+y) }
这里演示的是 A类的 x+y 操作符重载。细节应该不用多说。
打印结果:
[i=3 , j=3]
再来一个较为复杂的栗子, 重载 a[i]
/** * 比如,B类中有一个成员,list,我想重载操作符,直接取到list中的元素 */ class B { val list: MutableList<Int> = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9) } //a[i] operator fun B.get(i: Int): Int { return list[i] } fun main() { val b = B() println("${b[2]}") }
打印结果:
3
最后一个栗子:a > b,对应函数为:a.compare(b)
/** * 学生class */ data class Student(val math: Int = 0, val chinese: Int = 0, val english: Int = 0) fun Student.toString():String{ return "[math:${math} chinese:${chinese} english:${english}]" } fun Student.totalScore(): Int { return math + chinese + english } /** * 比如,我们要直接比较两个学生的总分 */ operator fun Student.compareTo(s: Student): Int { return this.totalScore() - s.totalScore()//比较2个学生的总分 } fun main() { val s1 = Student(math = 50, chinese = 90, english = 100) val s2 = Student(math = 80, chinese = 70, english = 60) println("s1:${s1}") println("s2:${s2}") //比如存在这两个学生,我要知道他们的总分谁高谁低 println("学生s1,s2的总分:${if(s1 > s2) "s1比较高" else "s2比较高" }") }
打印结果:
s1:Student(math=50, chinese=90, english=100) s2:Student(math=80, chinese=70, english=60) 学生s1,s2的总分:s1比较高
通过以上几个栗子,应该能看出,kotlin的操作符重载,编码和使用都十分简单。重载之前需要确定两件事
===
的重载,不支持三元操作符bool?a:b
这种 。####小编宅家期间整理了约 300G 关于BATJ大厂的面试资料
在这个部分总结了2019年到目前为止Java常见面试问题,取其面试核心编写成这份文档笔记,从中分析面试官的心理,摸清面试官的“套路”,可以说搞定90%以上的Java中高级面试没一点难度。
本节总结的内容涵盖了:消息队列、Redis缓存、分库分表、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。
目录:
(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)
部分内容:
对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。
不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注后,点击这里免费获取!
、读写分离、设计高并发系统、分布式系统、高可用系统、SpringCloud微服务架构等一系列互联网主流高级技术的知识点。
目录:
[外链图片转存中…(img-acxZuuZc-1623419626087)]
(上述只是一个整体目录大纲,每个点里面都有如下所示的详细内容,从面试问题——分析面试官心理——剖析面试题——完美解答的一个过程)
[外链图片转存中…(img-rBZyYK8o-1623419626088)]
部分内容:
[外链图片转存中…(img-IZOTKdIC-1623419626089)]
[外链图片转存中…(img-EAHpdn6P-1623419626090)]
[外链图片转存中…(img-I5AMZ0qT-1623419626091)]
对于每一个做技术的来说,学习是不能停止的,小编把2019年到目前为止Java的核心知识提炼出来了,无论你现在是处于什么阶段,如你所见,这份文档的内容无论是对于你找面试工作还是提升技术广度深度都是完美的。
不想被后浪淘汰的话,赶紧搞起来吧,高清完整版一共是888页,需要的话可以点赞+关注后,点击这里免费获取!