一、基本数据类型
分一下几种进行介绍。
- 数值类型
- 字符类型
- Boolean
1.1 数值类型
Kotlin 的基本数值类型包括 Byte
、Short
、Int
、Float
、Long
、Double
数据类型 | 位长 |
---|---|
Byte | 8 |
Short | 16 |
Int | 32 |
Float | 32 |
Long | 64 |
Double | 64 |
分一下几种情况分别进行说明:
- 数值类型使用;
- 数值类型 二、八、十六进制转换;
- 数值比较;
- 数值类型转换;
- 数值类型位操作;
1.1.1 数值类型使用
基本数值类型( Byte
、Short
、Int
、Float
、Long
、Double
)使用。
数值写法:
// Int:数值类型变量定义
// var <标识符> : <类型> = <初始化值>
val aa: Int = 123
val aa = 123
// Float: 使用 f 或者 F 后缀
val aa = 123.5f
val bb = 123.5F
// Long: 以大写的 L 结尾
val aa = 1234_5678_9012_3456L
// Double 默认写法
val aa = 123.5
1.1.2 二、八、十六进制
基本数值类型,二、八、十六进制支持情况。
// 十进制:123
val aa = 123
// 二进制:0b 开头
val aa = 0b00001011
// 八进制:暂不支持
// 十六进制:0x 开头
val aa = 0x0F
1.1.3 数值比较
Kotlin 中没有基础数据类型,只有封装的数字类型。
我们每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针,所以在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中:
- 三个等号
===
表示比较对象地址
; - 两个
==
表示比较两个值大小
。
// 地址空间比较(同一对象)
val a: Int = 10000
println(a === a) // true
// 地址空间比较(不同对象)
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA === anotherBoxedA) // false 对象地址不一样
// 大小比较
println(boxedA == anotherBoxedA) // true,值相等
1.1.4 类型转换
由于不同的表示方式,较小类型并不是较大类型的子类型。
因此,较小的类型不能隐式转换为较大的类型,因此若 Byte 转换为 Int 需进行显示转换。
val b: Byte = 1
// 显示转换
val i: Int = b.toInt()
每种数据类型都有下面的这些方法,可以转化为其它的类型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
1.1.5 位操作
// 左移位
shl(bits)
// 右移位
shr(bits)
// 无符号右移位
ushr(bits)
// 与
and(bits)
// 或
or(bits)
// 异或
xor(bits)
// 反向
inv()
1.2 字符类型
Char 必需是单引号 ’ 包含起来的。比如普通字符 ‘0’,‘a’。
// 地址空间比较(同一对象)
val a: Char = '9'
// 显示转换
val i: Int = a.toInt()
1.3 字符串
字符串变量声明:
// 单行字符串
val str = "123"
// 多行字符串
val text = """
多行字符串
多行字符串
"""
字符串使用方式:
// 取字符串中的字符
val str = "123"
println(str[2])
// 取字符串中的字符
for (c in str) {
println(c)
}
// 显示字符转换
val i: Int = str.toInt{)
// 去处空格
val text = """
多行字符串
多行字符串
""".trimMargin()
字符串模板:
// $ 表示一个变量名或者变量值
// $varName 表示变量值
// ${varName.fun()} 表示变量的方法返回值
fun main(args: Array<String>) {
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, now is $a"
print(s2)
}
1.3 布尔
布尔用 Boolean
类型表示,它有两个值:true 和 false。
val b: Boolean = true
二、变量
- 变量定义
- 变量NULL检查
- 变量类型检测
is
- 变量区间表示
- 可变长参数
vararg
2.1 变量定义
- var 可变变量;
- val 只读变量,相当于java中的final变量,val 变量创建的时候必须初始化。
var <变量标识符> : <变量类型> = <变量初始化值>
// 可变变量初始化:
var a: Int = 1
// 只读变量初始化:
val b: Int = 2
2.2 变量NULL检查
- 类型后面加? 表示可为空
var aa: String? = "23"
- 标识符后加!! 表示若为null 正常抛出异常;
val aa = age!!.toInt()
- 标识符后加? 表示若为空 则返回空
val aa = age?.toInt()
- ?:使用:age为空返回-1
val ages2 = age?.toInt() ?: -1
2.3 变量类型检测
// 类型检测
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
2.4 变量区间
- 使用 … 代表
rangeTo
(从小到大)for (i in 1..4) print(i) // 输出“1234”
- 使用
downTo
(从大到小)for (i in 4 downTo 1 step 2) print(i) // 输出“42”
- 使用
step
指定步长for (i in 1..4 step 2) print(i) // 输出“13”
- 使用
until
排除结束元素
// i in [1, 10) 排除了 10
for (i in 1 until 10) {
println(i)
}
2.5 可变长参数
可边长参数 vararg
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
// 测试
fun main(args: Array<String>) {
vars(1,2,3,4,5) // 输出12345
}
三、方法
- 方法定义
- lambda匿名方法
3.1 方法定义
方法定义使用关键字:fun
,
参数格式为:参数 : 类型
// 有返回值方法定义
fun sum(a: Int, b: Int): Int {
// Int 参数,返回值 Int
return a + b
}
// 无返回值方法定义
fun printSum(a: Int, b: Int) {
print(a + b)
}
3.2 lambda匿名方法
// 测试
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
四、条件控制
- if表达式
- when表达式
4.1 if表达式
一个 if 语句包含一个布尔表达式和一条或多条语句。
// 使用if else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 使用 if 表达式
var max = a
if (a < b) max = b
// 作为表达式使用:表达式结果赋值给变量
val max = if (a > b) a else b
// 作为表达式使用:表达式结果赋值给变量
val max = if (a > b) {
a
} else {
b
}
4.2 when表达式
when
将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。when
类似其他语言的 switch
操作符。when
表达式 else
同switch 的 default
,如果其他分支都不满足条件将会求值 else 分支。
//
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x 不是 1 ,也不是 2")
}
}
//
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
when
表达式,用in
检测一个值在或者不在区间或者集合中:
//
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
when
表达式,用用is
检测一个值是或者不是一个特定类型的值:
//
when(x) {
is String -> x.startsWith("prefix")
else -> false
}
五、循环控制
- for 循环
- while 与 do while
- return、break与continue
5.1 for 循环
for 循环可以对任何提供迭代器(iterator)的对象进行遍历。
//
for (item in collection) print(item)
// 循环体也可以是一个语句
val items = listOf("apple", "banana", "kiwi")
for (item in items) println(item)
// 循环体也可以是一个代码块
val items = listOf("apple", "banana", "kiwi")
for (item in items) {
println(item)
}
// 通过索引遍历一个数组或者一个 list
for (i in array.indices) {
print(array[i])
}
// 使用 withIndex 库方法
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
5.2 while 与 do while
while
和 do while
功能相似;不同的是 do while
至少会执行一次。
fun main(args: Array<String>) {
// while 循环
var x = 5
while (x > 0) {
println( x--)
}
// do…while 循环
var y = -1
do {
println(y--)
} while(y>0)
}
// 执行结果
// while 循环
5
4
3
2
1
// do…while 循环
-1
5.3 return、break与continue
- return 从最直接包围它的方法或者匿名方法返回;
- break 终止最直接包围它的循环;
- continue 继续下一次最直接包围它的循环;
使用方式举例如下:
fun main(args: Array<String>) {
for (i in 1..10) {
if (i==3) continue // i 为 3 时跳过当前循环,继续下一次循环
println(i)
if (i>5) break // i 为 6 时 跳出循环
}
}
Kotlin中的标签
在 Kotlin 中任何表达式都可以用标签来标记。标签的格式
为 标识符后跟 @
符号,例如:abc@、fooBar@都是有效的标签。
// 用标签限制 break
loop@ for (i in 1..100) {
for (j in 1..100) {
if (……) break@loop
}
}
六、类
- 类定义
- 抽象类
- 嵌套类
- 内部类
- 匿名内部类
6.1 类定义
- 类关键词
类使用关键字class
声明,后面紧跟类名:class Person{}
- 主构造方法
类可以有一个主构造器
、多个次构造器
。
主构造器是类头部的一部分,位于类名称之后。主构造器中不能包含任何代码,初始化代码可以放在初始化代码段init
中。 - 次构造方法
如果类有主构造方法,每个次构造方法都要 直接或间接通过另一个次构造方法 代理主构造方法;在同一个类中代理另一个构造方法使用 this 关键字; - 类属性声明
类中非空属性必须在定义的时候初始化
,但也可通过lateinit
关键词延迟初始化。
另外,Kotlin提供了后端变量field
机制,但field关键词
只能应用于属性的访问器。
Kotlin 类构造代码举例如下:
// 类关键词:类使用关键字 class 声明,后面紧跟类名;
// 构造方法:带构造方法的类 (constructor关键词 可省略);
class Person constructor (name: String){
// 类属性:非空属性必须在定义的时候初始化;
// 类属性:Kotlin提供了 后端变量field 机制,但 field关键词 只能应用于属性的访问器;
var mName: String = "xia"
get() = field.toUpperCase() // 将变量赋值后转换为大写
var mAge: Int = 16
set(value) {
if (value < 20) { // 如果传入的值小于 20 返回该值
field = value
} else {
field = 18 // 否则返回18
}
}
// 构造方法:主构造器中不能包含任何代码,初始化代码可放在 init 代码段中;
init {
this.mName = name
println("init name $mName")
println("init age $mAge")
}
// 次构造方法:如果类有主构造方法,每个次构造方法都要 直接或间接通过另一个次构造方法 代理主构造方法;
// 次构造方法:在同一个类中代理另一个构造方法使用 this 关键字;
constructor (name: String, age:Int) : this(name) {
// 次构造方法...
this.mName = name
this.mAge = age
println("constructor init name $mName")
println("constructor init age $mAge")
}
}
// main 程序入口
fun main(args: Array<String>) {
// 创建对象
var person: Person = Person("xiaxl")
// 姓名
person.mName = "xiaxueliang"
println("changeName:${person.mName}")
// 年龄
person.mAge = 21
println("changeAge:${person.mAge}")
}
6.2 抽象类
抽象类 类本身、类中的部分成员,都可以声明为abstract类型。
// 抽象类 类本身、类中的部分成员,都可以声明为abstract类型
abstract class Person {
abstract fun changeName()
}
6.3 嵌套类
可以把类嵌套在其他类中。
// 嵌套类:可以把类嵌套在其他类中
// 外部类
class Outer {
private val outerProperty: Int = 1
// 嵌套类
class Person {
fun changeAge() = 18
}
}
fun main(args: Array<String>) {
// 嵌套类调用:外部类.嵌套类.嵌套类方法/属性
val age = Outer.Person().changeAge()
println(age) // 输出 18
}
6.4 内部类
内部类使用 inner
关键字来表示。
内部类会带有一个对外部类的对象的引用,所以可以访问外部类成员属性和成员方法。可以使用 this@label
标签,访问来自外部作用域的 this
。
// 内部类:内部类使用 inner 关键字来表示。
// 内部类:内部类会带有一个对外部类的对象的引用,所以可以访问外部类成员属性和成员方法。
class Outer {
private val iProperty: Int = 18
var sProperty = "outerName"
// 内部类
inner class Person {
// 访问外部类成员
fun test01() = iProperty
// 获取外部类对象,访问外部类成员
fun test02() {
// 内部类:使用 `this@label` 标签,访问来自外部作用域的 `this`
var outerObj = this@Outer
// 访问外部类成员
println("inner sProperty:" + outerObj.sProperty)
}
}
}
fun main(args: Array<String>) {
val iProperty = Outer().Person().test01()
println("main Person test01: " + iProperty)
//
val sProperty = Outer().Person().test02()
println("main Person test02: " + sProperty)
}
6.5 匿名内部类
// 定义接口
interface PersionInterFace {
fun changeName()
}
// 定义类
class Persion {
var v = "成员属性"
// 定义方法
fun setInterFace(testObj: PersionInterFace) {
testObj.changeName()
}
}
// 程序入口
fun main(args: Array<String>) {
// 获取类的对象
var persion = Persion()
// 采用对象表达式来创建接口对象,即匿名内部类的实例。
persion.setInterFace(object : PersionInterFace {
override fun changeName() {
println("实现匿名内部类的 changeName 方法")
}
})
}
七、继承
Kotlin 中所有类都继承该 Any
类,它是所有类的超类。
另外,Any
类默认提供了三个方法,分别为:equals()
、hashCode()
、toString()
。
- 构造方法
- 重写方法
- 重写属性
7.1 构造方法
- 如果子类有主构造方法:
则必须在子类主构造方法
中初始化基类
。
// 如果子类有主构造方法:则必须在子类主构造方法中初始化基类。
// 基类
// 如果一个类要被继承,可以使用 open 关键字进行修饰
// Any 类为所有类的超类
open class Person(var name:String, var age:Int){
}
// 子类 Student
class Student(name:String, age:Int, ,number:String) : Person(name, age) {
}
// 测试
fun main(args: Array<String>) {
var s = Student("xia", 18, "n987456123")
println("姓名: ${name}")
println("年龄: ${age}")
println("学号: ${number}")
}
- 如果子类没有主构造方法:
则必须在子类每一个次级构造方法
中用super
关键字初始化基类
。
// 如果子类没有有主构造方法:则必须在子类每一个次级构造方法中用 super 关键字初始化基类。
// 基类
open class Person(name:String){
// 基类 次构造方法
constructor(name:String,age:Int):this(name){
println("--- 基类 次构造方法 ---")
println("姓名: ${name}")
println("年龄: ${age}")
}
}
// 子类 Student
class Student:Person{
// 子类 次构造方法
constructor(name:String,age:Int,number:String):super(name,age){
println("--- 子类 次级构造方法 ---")
println("姓名: ${name}")
println("年龄: ${age}")
println("学号: ${number}")
}
}
fun main(args: Array<String>) {
var s = Student("xia", 18, "number987456123")
}
7.2 重写方法
在基类中,声明函数方法时,此方法默认为 final
,不能被子类重写。
如果允许子类重写该方法,需要手动添加 open
修饰, 子类重写方法需使用 override
关键词。
// 基类
// 方法重写:在基类中,使用fun声明函数方法时,此函数方法默认为 final ,不能被子类重写。
// 方法重写:如果允许子类重写该方法,需要手动添加 open 修饰, 子类重写方法需使用 override 关键词。
open class Person{
// 允许子类重写
open fun study(){
println("学习使我快乐")
}
}
// 子类
class Student : Person() {
// 重写
override fun study(){
println("我是一名学生")
}
}
// 程序入口
fun main(args: Array<String>) {
val s = Student()
s.study();
}
7.3 重写属性
使用 override 关键字,也可以在主构造函数中使用 override 关键字作为属性声明的一部分。
interface Persion {
val age: Int
}
// 重写属性:使用 override 关键字
class Student1 : Persion {
override var age: Int = 9
}
// 重写属性:在主构造函数中使用 override 关键字作为属性声明的一部分
class Student2(override val age: Int) : Persion{
}
// 程序入口
fun main(args: Array<String>) {
var s1 = Student1()
println("Student1: "+s1.age);
var s2 = Student2(18)
println("Student2: "+s2.age);
}
八、接口
Kotlin 使用 interface 关键字定义接口。
- 接口中的方法允许有默认实现;
- 接口中的属性只能是抽象的,不允许初始化;
- 一个类可以实现一个或多个接口,实现多个接口时,可能会遇到同一方法继承多个实现的问题。
// 接口类A
interface AInterface {
// 接口:属性只能是抽象的,不允许初始化
var name:String
// 接口:方法允许有默认实现
// 未实现的方法
fun changeName()
// 已实现的方法
fun changeAge() {
println("AInterface changeAge")
}
}
// 接口类B
interface BInterface {
// 接口:方法允许有默认实现
// 未实现的方法
fun changeName() {
println("BInterface changeName")
}
// 已实现的方法
fun changeAge() {
println("BInterface changeAge")
}
}
class C : AInterface {
// 重写属性
override var name: String = "xiaxl"
// 重写方法
override fun changeName() {
println("Class C changeName")
}
}
class D : AInterface, BInterface {
// 重写属性
override var name: String = "xiaxl"
// 重写方法
override fun changeName() {
// super<AInterface>.changeName()
super<BInterface>.changeName()
println("Class D changeName")
}
// 重写方法
override fun changeAge() {
super<BInterface>.changeAge()
println("Class D changeAge")
}
}
fun main(args: Array<String>) {
val d = D()
println(" ")
d.changeName();
println(" ")
d.changeAge();
}
九、枚举
enum class Color(val rgbValue: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
fun main(args: Array<String>) {
//
var color:Color=Color.GREEN
// 打印枚举名称
println(color.name)
// 打印枚举顺序
println(color.ordinal)
// 打印枚举 Value
println(color.rgbValue)
}
十、数据类型
先说为什么要使用数据类
:
Kotlin使用数据类替换普通类的好处是Kotlin会帮我们产生大量代码,省去大量的重复性工作。
- Kotlin中使用数据类只需要在
class
关键字前添加data
关键字,编译器会自动从主构造函数中根据声明的属性推导出相关函数equals()
、toString()
、copy()
、componentN()
、hashCode()
; - Kotlin中数据类 自动声明与构造函数入参同名的属性字段,自动实现每个属性字段的存取器方法
set/get
;
声明数据类:
// 声明数据类型
data class <类名> <主构造函数参数> <:继承类和实现接口> { /*类体*/ }
数据类型举例:
// 创建数据类
data class Person(var name :String, var age :Int){
// 类体内生命属性
var job: String = "student"
}
// 程序入口
fun main(args: Array<String>) {
var p = Person("xiaxl",18)
// 调用数据类的 getter
println(p.name)
println(p.age)
println(p.job)
// 调用数据类的 setter
p.name = "xiaxueliang"
// 自动调用toString()方法
println(p)
//调用hashCode()方法,输出内存地址
println(p.hashCode())
// 解析构造函数 componentN
val (name, age) = p
println("name = $name, age=$age")
}
十一、泛型
// 泛型举例
class Persion<T>(t : T) {
var value = t
}
// 泛型举例
fun <T> doPrintln(t: T) {
when (t) {
is Int -> println("整型数字: $t")
is String -> println("字符串大写: ${t.toUpperCase()}")
else -> println(" 不是整型,也不是字符串")
}
}
// 程序入口
fun main(args: Array<String>) {
var pInt = Persion<Int>(18)
var pString = Persion<String>("xiaxl")
// 输出
doPrintln(pInt.value)
doPrintln(pString.value)
}
十二、伴生对象
- 伴生对象
- 对象声明
12.1 伴生对象
伴生对象,就是随着类的存在而存在。
在Kotlin中,类是没有static方法的,在大多数情况下 Kotlin 推荐的做法是使用包级别的函数作为静态方法,Kotlin 会将包级别的函数当做静态方法来看待。
// 伴生对象
class MyConstant {
companion object {
var num: Int = 100
fun doPrint() {
println("method test")
}
}
}
// 程序入口
fun main(args: Array<String>) {
// 变量
var num = MyConstant.num
println("num: $num")
// 方法
MyConstant.doPrint()
}
12.2 对象声明
Kotlin 可以通过对象声明
来获得一个单例
,Kotlin 使用 object
关键字来声明一个对象。
// 对象声明
object Singleton{
// 属性
val name = "xia"
// 方法
fun doPrint(age: Int){
println("$name ---- $age")
}
}
// 程序入口
fun main(args: Array<String>) {
// 单例
Singleton.doPrint(18)
}