[TOC]
kotlin 基础
java 中有 main()
方法作为程序的入口,那么在 kotlin 里边也是类似的,也是提供了一个入口函数,如下:
fun main(args: Array<String>) {
println("Hello World")
}
在 kotlin 定义一个函数(或者称之为方法)是以fun
开头的,这有点类似于 JS 里边的 function 似的.其中的args: Array<String>
指的是这个函数的参数,在 kotlin 中,参数类型是放在后边而参数名是在前边的,这个跟 java 中是正好相反的.
println(message)
就是 kotlin 中的输出语句,内部实际调用了System.out.println(message)
.
基本语法
注释
// 这是单行注释
/*
* 这是多行注释
*/
/**
* 这是文档注释
**/
变量
kotlin 中的变量,分为可变变量
和不可变变量
.
可变变量用var
修饰,一次赋值,支持多洗修改;不可变变量用val
修饰(等价于 java 中用 final 关键字修饰的变量),一次赋值,不可更改.
kotlin 声明变量的时候,类型是在后边声明的.
举例:
var a: String = "a" // 声明一个 String 类型的变量 a
val b: String = "b" // 声明一个 String 类型的常量 b,不可重新赋值
a = "aa" // 没问题
b = "bb" // 会报错
变量的数据类型
kotlin 中的数据类型,分为数值型,字符型,布尔型,数组性,字符串型等
数值类型
Byte,Float,Double,Int,Long,Short
布尔类型
Boolean
字符类型
Char
字符串类型
String
数组类型
常规使用是Array
,但是针对某些特殊的比如 Int,Boolean 的数组,提供了特殊的格式IntArray
,BooleanArray
举例说明:
var byte: Byte
var float: Float = 1.0F
var double: Double = 2.0
var int: Int = 3
var long: Long = 33
var short: Short = 4
var boolean: Boolean = false
var char: Char = 'c'
var string: String = "string"
var intArray: IntArray = intArrayOf(1, 2, 3)
var booleanArray: BooleanArray = booleanArrayOf(false, true)
var stringArray: Array<String> = arrayOf("a", "b", "c")
运算符
算术运算符,赋值运算符,比较运算符,逻辑运算符.跟 java 中没什么区别
字符串
定义同 java 中的字符串,定义以及普通用法一样一样的...
val s = "Hello Kotlin" // 定义一个字符串
println(s)
println(s[0]) // 根据索引值来获取对应位置上的字符
for (c in s) { // 对字符串进行遍历
print("$c,")
}
println()
// 同时,kotlin 标准库中也提供了对应的字符串遍历的方法。
s.forEach {
print("$it,")
}
kotlin 中一些增加的方法.
字符串查找
println(s.first()) // 获取第一个字符
println(s.last()) // 获取最后一个字符
println(s.get(1)) // 获取索引值为 1 的字符
println(s.lastIndexOf("l")) // 获取指定字符最有一次出现的索引值
字符串截取
方法同 java 中的 subString()
字符串替换
同 java 中的 replace()
模板表达式
java 中要使用一个变量,需要用+号拼起来用,如下
String a = "a";
String ab = a + "b";
但是 kotlin 中可以使用$
来实现模板化,可以直接在双引号之间引入你所需要的变量.单个引用可以省略掉{}
val a = "a"
val b = "${a}b"
println("b = $b") // b = ab
选择结构语句
if 语句和 when(等同于 java 中的 switch/case) 语句
if 语句
if 语句的使用,跟 java 中一模一样
// if 语句
val point = 60
if (point < 60) {
println("不及格")
} else if (60 < point && point < 90) {
println("良好")
} else {
println("优秀")
}
三元运算符
kotlin 中不存在三元运算符,但是可以使用如下实现:
val result = if (point < 60) "淘汰" else "过关"
when 语句
when 语句其实就是 java 中的 switch/case 语句.
// when 语句
val season = 3
val seasonResult = when (season) {
1 -> "春季"
3 -> "春季"
6 -> "夏季"
12 -> "冬季"
else -> "未知季节"
}
示例代码中,将 when 语句的返回值赋给了一个变量,然后进行输出,可以看到, ->
符号前边的就是各种 case,后边的就是需要匹配的内容.
同时也支持多个 case 匹配,比如 1,2,3 都是春季,那么可以采用如下写法:
val seasonResult = when (season) {
1, 2, 3 -> "春季"
4, 5, 6 -> "夏季"
else -> "其他季节"
}
循环结构语句
while,do..while 等循环结构都跟 java 中一样,使用方法也是一样的.主要学一下 for 循环.
kotlin 中的 for 循环是这样的.
for(循环对象 in 循环条件) {
执行语句
}
其中的in
其实是 kotlin 中的范围,
普通 for 循环
下边的代码就是循环 4 次,输出 4 次日志.其中的in
指的是范围,1..4
中的..
指的是区间
,等价于大于等于 1,小于等于 4
,所以最后的实际输出是 4 次.
// for 循环
for (i in 1..4) {
println("string--->$i")
}
forEach(),forEachIndexed()
kotlin 中还提供了两个增强类型的for 循环,主要针对数组和集合,但是所有对象都可用.
forEach{}: 接收一个闭包参数
val forArray = arrayOf("a", "b", "c")
forArray.forEach {
println("forEach----$it")
}
forEachIndexed{}: 两个参数,第一个是角标;第二个是元素
forArray.forEachIndexed { index, it ->
println("forEachIndexed----index=$index it=$it")
}
区间
区间指的是一类数据的集合,比如在数学中的[1,3],[1,3)等.
rangeTo(Int) & ..
rangeTo(Int)函数构成了区间表达,可以用..
操作符来简写
// 区间
val qujian = 1.rangeTo(3) // 等价于 1 <= qujian <= 3
for (index in qujian) {
println("区间的值:$index")
}
此处可以把rangeTo()
替换成..
操作符来实现,效果一致
until 操作符
假如我要实现 1 <= index < 3 ,kotlin 里边怎么写?
在 kotlin 中,有个 until
操作符,效果可以实现右边开区间的效果,如下:
// 输出结果只有 1,2 两个值,因为右边不包含 3
for (index in 1 until 3) {
println("until的值:$index")
}
downTo(Int)
要是想实现逆序遍历区间呢?
kotlin 里边提供了downTo(Int)
方便的实现逆序区间.
// 输出:3,2,1
for (index in 3.downTo(1)) {
println("区间的值:$index")
}
步长 step
前边说的区间,遍历的时候都是步长是 1 的,也就是保证输出是 1,2,3... 假如我要实现输出 1,3,5...呢?
kotlin 里边提供了step
关键字,支持设置步长.使用很简单,就是在循环条件之后加上step
关键字,然后指定你需要的步长就行了,比如下边是保证了输出奇数...
// 输出 1,3,5,7,9
for (index in 1..10 step 2) {
println("step = $index")
}
数组
kotlin 中的数组,用 Array 表示,通过泛型来控制数组中的元素类型.
// 数组
val array = arrayOf<String>("a", "b", "c")
// 通过[]直接获取索引值处的元素
println("索引值为 1 的元素是:${array[1]}")
println("索引值为 1 的元素是:${array.get(1)}")
println("数组的长度是:${array.size}")
数组常见操作
遍历
kotlin 中常见的遍历方法有 for 循环遍历和 forEach 遍历,同时都提供了按照索引值遍历的方案.
// 遍历
array.forEach {
println("数组元素:$it")
}
array.forEachIndexed { index, content ->
println("数组元素:index=$index content=$content")
}
for (content in array) {
println("数组元素(for 循环):$content")
}
for ((index, content) in array.withIndex()) {
println("数组元素(withIndex):index=$index content=$content")
}
修改数组的值
kotlin 的数组中提供了set(index, value)
方法用来修改指定索引值上的值,同时可以简写为[]
array.set(1, "bb")
array[1] = "bb"
获取元素的索引值
通过indexOf(value)
可以获得指定元素在数组中的索引值.
println("值为 b 的索引值是:${array.indexOf("b")}")
变量类型转换
kotlin 中的类型转换,包括智能类型转换和强制类型转换.
类型检查
kotlin 中可以采用is
或者!is
来判断是否是某种类型的.
// 类型判断
val typeBoolean: Boolean = false
println("typeBoolean 的类型是:${typeBoolean is Boolean}")
智能类型转换
在 kotlin 中,经过is
类型判断之后,kotlin 会智能的转换成当前判断的类型
强制类型转换
在 kotlin 中可以采用as
关键字实现强制类型转换,同时可以使用as?
进行安全的类型转换.
val aStr = "aaaa"
val bString = aStr as String
println("bString 的类型是:${bString.javaClass}") // class java.lang.String
接下来我尝试将一个 Int 类型的转换成 String 类型的试试
val aInt = 1
val bInt = aInt as String
println(bInt)
此时会报错:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at com.yanftch.review.kotlinbasic.KtKt.main(Kt.kt:137)
这就是类转换异常的报错,那么在 kotlin 中该怎么避免你呢? 这就是下边说的as?
关键字了,他可以在上述情况下避免抛出异常.
所以上边的代码,如果采用 as? 的话,最后会正常输出,但是输出的值是 null
val aInt = 1
val bInt = aInt as? String
println(bInt) // 输出 null
空值处理
空指针异常是最常见的开发异常,那么 kotlin 中是怎么处理空指针异常呢?
可空类型变量 ?
在 kotlin 可以通过?
指定一个变量是可空的变量,可空变量可以赋值为 null,
var name: String // 不可空变量
var age: Int? // 可空变量
上边的代码中,制定了 name 为不可空变量,那么如果给赋值 null 就会报错,相反,age 变量指定了为可空变量,那么就可以赋值为 null.
安全调用符 ?.
当一个变量为可空的时候,我们在使用的时候,需要对他做空判断之后再用,kotlin 中可以直接使用?.
操作符进行可空类型的变量的调用,也不会出现任何问题.
val name: String? = null
print(name?.toString())
Elvis 操作符 ?:
采用 ?. 调用的时候,如果调用者是为空的,那么返回地同样也是空, 如果我不想要 null 而是想要一个默认值呢?那么很简单,可以采用?:
操作符, 他的用处是:调用者如果不为空,那么就返回该有的值(也就是?:前边的值);调用者为空,那么就返回设置好的默认值(也就是?:后边的值)
val name: String? = null
val nameResult = name ?: "default"
println("nameResult 的值是: $nameResult")
name 为空的时候,返回的值就是 "default",不为空,就返回 name 的实际值.
函数/方法
kotlin 中的函数,也就是方法,用处跟java 中一样,但是增加了一些自己的特性.
函数的定义
kotlin 中的函数,需要通过关键字fun
来定义. 通过一个简单的函数来学习
fun method(a: Int, b: Int): Int {
val sum = a + b
return sum
}
- fun: 函数的命名关键字
- method: 函数的名字
- (a: Int, b: Int): 表示的是两个参数,其中分号前边是参数名称,后边是参数类型,多个参数之间用逗号分开
- Int: 大括号前边的这个 Int 表示的是当前函数的返回值类型.
- val sum = a + b: 函数体
- return sum: 表示的是函数的返回值.
如果一个函数不返回任何类型,那么实际是返回的Unit
类似 java 中的void
.可省略不写.
函数的类型
函数整体分为:有(无)参函数; 有(无)返回值函数
kotlin 中,如果函数体只有一行,那么可以省略返回值类型以及大括号,用一行代码来实现,如下:
fun getAge() = 18
函数的参数
函数的参数,主要将具名参数和默认参数.
具名参数
就是指的在使用的时候,显示的表明参数的名称,这样可以忽略参数的顺序
// 具名函数
fun method2(name: String, age: Int) {
println("name=$name, age=$age")
}
// 调用
method2(age = 18, name = "tom")
默认参数
java 中可以通过方法重载,实现同名函数,不同参数,在 kotlin 中可以使用一个函数来实现类似重载的作用.
就是在函数的定义的时候,给某些指定的参数声明默认值.比如上边的函数,可以给 age 指定默认为 20,那么就算是后续调用函数的时候,不设置 age 的值,他的输出也是 20
fun method2(name: String, age: Int = 20) {
println("name=$name, age=$age")
}
// 调用
method2(name = "tom") // 输出:name=tom, age=20
面向对象
会 java 的,面向对象还需要说吗?~~~
类与对象
类定义
声明一个类,也是使用class
关键字
class User1 constructor(name: String) {
private var mName: String? = null
private var mAge: Int = 0
fun user1Method() {
println("name=$mName, age=$mAge")
}
}
mName, mAge: 类属性或者类成员变量
user1Method: 类方法
对象创建
在 java 中需要 通过 new
关键字来实例化一个对象,但是在 kotlin 可以省略掉他.
val user1 = User1()
然后可以对应的调用类的成员变量和函数
user1.mAge
user1.user1Method()
构造函数
构造函数可以对类中的一些变量进行初始化操作赋值. kotlin 可以直接在类定义的时候指定构造函数,称之为主构造,然后可以在内部创建次要构造.
主构造:
用constructor
修饰,作用于类头和类名之间,如果没有参数的话可以省略.
class User1 constructor(name: String) {}
如上就是通过主构造函数给当前类传递了一个参数.
name 参数的使用的话,就是可以直接在 kotlin 中的init{}
代码块中进行处理.
class User1 constructor(name: String) {
private var mName: String? = null
init {
this.mName = name
}
}
次要构造
一个类可以有多个次要构造函数.
也需要用那个关键字修饰
// 定义次构造函数,传递两个参数,并调用主构造函数
constructor(name: String, age: Int) : this(name) {
this.mAge = age
}
this 关键字
作用同 java 中
类继承
让子类拥有父类的特性.java 中通过extends
关键字实现子类继承父类,kotlin 中通过:
来实现.
kotlin 中所有的类默认是final 的,不能被继承,所以你需要对你要继承的类用 open 关键字修饰
class User2 constructor(name: String) : User1(name) {
}
// User1 只有一个构造函数,传参 name,那么子类继承时需要传.
- 单继承:一个类只能有一个直接父类
- 支持多层继承
函数重写
方法的重写,存在于子类和父类之间,同 java 中,需要override
修饰.
Any 和 Object
在 java 中,所有类的根本父类是 Object, 但是 Kotlin 中的根本父类是Any
, Any 在运行时,会自动映射成 Object 对象.
常见类
嵌套类
kotlin 中,一个类内部再声明一个类, 成为嵌套类,嵌套类默认不持有外部的引用,因此不能访问到外部的变量或者函数,如果希望可以引用,需要将内部的类,用inner
关键字修饰.
如下代码中, 在嵌套类里边会报错,提示找不到那两个参数.
class Outer {
private var mName: String? = null
private var mAge: Int = 0
class Inner {
init {
println("name=$mName, age=$mAge")
}
}
}
通过字节码反编成 java 文件,发现,不用inner
修饰的内部的类,其实等同于 java 中的静态内部类.加了修饰之后,就是普通的内部类.
数据类
在java 中,定义一个类,要想外部访问,需要提供 set/get 方法,但是在 kotlin 中,只需要在类声明的时候在class 前边加上data
关键字修饰就行了, 内部自动提供 set/get 方法.
data class DataClassModel(var name: String, var age: Int)
// 其他地方调用
println("${dataClassModel.age}, ${dataClassModel.name}")
DataClassModel 实例化之后,可以直接通过.
来获取对应的属性.