image.png

Kotlin简介

背景

Kotlin是JetBrains开发的基于JVM的语言,集合了众多主流编程语言,如Java、C、Python、Javascript等的优点。Kotlin的创建来源于Java开发者思维,可与Java实现100%互操作,同时也非常适合开发Android。

发展历程

Kotlin发展历程.png

  1. # 版本分布

JVM语言原理

JVM语言原理.png


Kotlin特性

Kotlin VS Java,有以下优势:

  • 简洁可读:代码更短、可读性更强;
  • 互操作性:Kotlin是基于JVM开发的,与JDK完全兼容,可与Java进行100%互操作;
  • 空安全:编译时期就已经处理了各种null情况,避免在执行时出现异常,因此减少了开发者处理null异常的时间和代码;
  • 函数式:函数可以像变量那样被定义、修改、传递,使用Lambda更方便,且运行比Java更快;
  • 扩展:给一个我们没有权限修改的第三方库扩展函数和属性,增强类的特性;
  • 多平台和多场景开发:Kotlin可用于服务端、Android、JavaScript、原生程序、数据科学;多平台并不是指为所有平台编译全部代码,而是建立一个多平台共享的API集合,这个集合力争可以包含更多代码,API的具体实现还是基于平台自身特性,借助expect/actual则可以访问平台API;
  • 协程

    *拓展——进程、线程和协程

  • 进程:系统资源分配的最小单位;进程的创建是以消耗系统资源为代价;进程的执行是抢占式的,单核CPU同一时间只能执行一个进程的代码;进程间相互隔离,各自拥有内存资源,相对线程更安全;

  • 线程:CPU调度的最小单位,进程的子单元,不占用系统额外资源;多线程共享进程的资源空间(内存地址),线程锁就是为了避免多线程享有同一地址变量带来的安全问题;当一个线程挂了,会导致整个进程崩溃;线程间相互通信和切换较为容易;
  • 协程:协程程序是在线程里面跑的,又称微线程或纤程;协程的调度切换需要用户手动切换,没有线程的上下文切换消耗。

举个例子,一群猴子去京东抢购桃子。如果该抢购程序是多线程执行的,那么猴子们一拥而上,谁网速快、手速快就能抢到,但有可能发生死锁导致程序崩溃;如果是协程 ,那商家可能就会存在暗箱操作,猴子A虽然先抢到,但系统检测到你猴品不好,暂停当前订单,先去照顾别的猴子,最后继续执行你剩下的任务。


数据类型

image.png

:::warning

  • Kotlin中的数字没有隐式拓宽转换,例如Float、Int类型不能直接赋给Double类型,否则会报错,Char类型也不能当作Int类型使用;
  • Kotlin里一切皆为对象,一切变量都能调用其属性和方法,但Java里还保留了int、long等声明方式;
  • 支持表达式类型提升;
  • 自动装箱不一定保留同一性(引用相等),但保留相等性(值相等)。比如,Int类型在 -128~127范围内保留同一性,其他范围只有相等性,与Java一致。 :::

整数

类型 位宽 最小值 最大值
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 (-2^31) 2,147,483,647 (2^31 - 1)
Long 64 -9,223,372,036,854,775,808 (-2^63) 9,223,372,036,854,775,807 (2^63 - 1)
  1. val number = 100 //默认是 Int 类型
  2. val bigNumber = 8000000000 //超过 Int 最大值默认是 Long 类型
  3. val longNumber = 20L //数字后面显式加L,表示声明 Long 类型
  4. val byteNumber:Byte = 1

字符串

:::warning 字符串模版

字符串字面值可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。 模板表达式以美元符($)开头,由一个简单的名字构成: :::

类型转换

在 Kotlin 中与 Java 不同是通过调用 toInt、toDouble、toFloat 之类函数来实现数字类型的强制转换的。

流程控制

  • if条件表达式
  • for循环语句
  • while和do..while循环语句
  • when表达式

类与对象

类和对象

类包含:构造器(主构造函数和次构造函数)、初始化块、函数、属性、嵌套类和内部类、对象声明
类声明:类名、类头(指定类型参数、主构造函数)和类体
构造函数:(自定义或默认)主构造函数和一个或多个次构造函数,其中主构造函数不能包含任何代码,初始化的代码放在以init关键字作为前缀的初始化块中
构造函数中也可以声明属性并给定默认值,而且如果主构造函数的参数都有默认值,系统还会额外创建一个默认的无参构造器
创建对象时,对于构造函数中已经给定默认值的参数可以不用再传递

  1. class Person{
  2. //...
  3. }
  4. class Empty
  5. /**
  6. * 声明Person类并指定主构造函数
  7. * 初始化代码放在初始化块中
  8. * 如果类有主构造函数,每个次构造函数都要,
  9. * 或直接或间接通过另一个次构造函数代理主构造函数。
  10. * 在同一个类中代理另一个构造函数使用 this 关键字
  11. */
  12. /**
  13. * 声明Person类并指定主构造函数
  14. * 初始化代码放在初始化块中
  15. * 如果主构造函数没有任何注解或者可⻅性修饰符,可以省略这个constructor关键字
  16. */
  17. class Person constructor(name: String, sex: String ,var age: Int) {
  18. //初始化块
  19. init {
  20. //注意初始化块中访问属性也是通过set/get
  21. age = if (age >150) {
  22. println("warning: age > 150")
  23. -1
  24. } else age
  25. }
  26. //属性初始化器
  27. val firstProperty = "First Property: $name".also(::println)
  28. init {
  29. println("Name: $name, $firstProperty")
  30. }
  31. val secondProperty = "Second Property: $sex".also(::println)
  32. init {
  33. println("Sex: $sex, $secondProperty")
  34. }
  35. var id: Int
  36. init {
  37. id = 0
  38. }
  39. constructor(id: Int) : this("", "", 0){
  40. this.id = id
  41. }
  42. //如果将field改为niceName会怎样
  43. var niceName: String = "q"
  44. get() = field
  45. set(value) {
  46. field = value
  47. }
  48. }
  49. fun main() {
  50. //创建类的实例
  51. var person = Person("Tom", "male", 151)
  52. println(person.age)
  53. var p2 = Person(10)
  54. println(p2.id)
  55. println(p2.niceName)
  56. }