# Kotlin教程 - 2 基础语法
# 2.1 字面量
Kotlin 是一种静态类型的编程语言,它具有丰富的数据类型来表示不同种类的数据,先介绍一下 Kotlin 中基本的数据类型。
如果没学过其他语言,看数据类型可能比较懵逼,数据类型就是说数据有哪些类型,例如我们保存姓名,使用的的是字符串,也就是文本,但是保存年龄,是数字类型,金额可能使用的小数的类型,不同的数据会使用不同的数据类型来保存。
整数类型:
下面这些都是整数:
123
1234567
123456789101112L // 表示长整型,需要以L作为后缀
123L // 表示长整型
0x0F // 十六进制形式整数
0b00001011 // 二进制形式的整数
2
3
4
5
6
Kotlin 中表示整数的类型有如下几种,他们的长度不同,所以可以表示的范围不同。
Byte
:8 位有符号整数,取值范围为 -128 到 127。Short
:16 位有符号整数,取值范围为 -32768 到 32767。Int
:32 位有符号整数,通常是最常用的整数类型。Long
:64 位有符号整数,用于表示大整数值,例如时间戳(时间的毫秒数)。
上面这样在代码中直接书写的数据,被称为字面量。
如何使用数据类型,讲到变量的时候再讲解,不必着急。
浮点数类型:
123.5 // 默认就是双精度浮点数
123.5e10 // 科学计数法,表示123.5乘以10的10次方,平时用的不多
123.5f // 单精度浮点数,后面需要加f后缀
2
3
Kotlin 中表示浮点数的类型有如下两种:
Float
:32 位单精度浮点数(小数),用于表示小数,有约 6-7 位有效数字。Double
:64 位双精度浮点数(小数),用于表示双精度小数,有约 15-16 位有效数字。
为了便于阅读,还可以在字面量重添加下划线
100_000_001
1234_5678_9012L
0xFF_EC_DE_5E // 十六进制
0b11010010_01101011_10011100_10010011 // 二进制
123_4567_7890.123 // 双精度浮点数
123_4567.1f // 单精度浮点数
2
3
4
5
6
字符类型:
'A'
'a'
'#'
'3' // 这不是整数,是一个字符
2
3
4
Char
:16 位 Unicode 字符,用于表示单个字符,例如字母、数字或符号。
和Java不同,Kotlin中Char只能表示单个字符,不能和数字直接进行转换。
布尔类型:
true
false
2
Boolean
:表示布尔值,只能取true
或false
。
字符串类型:
还有最常用的数据类型,就是字符串类型,用来存储文本数据。在Kotlin中,可以使用双引号 "
或三个双引号 """
来定义字符串:
"abc"
"""
你好,
China
"""
2
3
4
5
6
String
:表示字符串,可以包含任何字符序列。
Kotlin中还有一些其他的数据类型,例如空类型、数组类型、自定义数据类型(类、枚举类等),这些后面在讲解,这里就不说了。
在 Java 中,数据类型分为基本数据类型和引用数据类型,但是在 Kotlin 中只有引用数据类型,只是Byte、Short、Int、Long、Float、Double、Boolean、Char ,在编译的时候,还是会编译为基本数据类型来处理。
# 2.2 注释
我们在学习任何语言,都会有注释,注释的作用就是向别人解释我们编写的代码的含义和逻辑,使代码有更好的可读性,注释不是程序,是不会被执行的。
在Kotlin中注释分类三类,单行注释、多行注释、文档注释。
# 1 单行注释
单行注释以 //
开头,//
号右边为注释内容。
例如:
// 我是单行注释,打印Hello World
print("Hello World!")
2
注意:// 号和注释内容一般建议以一个空格隔开,这是代码规范,建议大家遵守。
单行注释一般用于对一行或一小部分代码进行解释。
# 2 多行注释
多行注释是以 /* */
括起来,中间的内容为注释内容,注释内容可以换行。
/*
我是多行注释,
可以跨越多行
*/
print("Hello World!")
2
3
4
5
多行注释一般用于描述一段代码或者变量的详细信息。
# 3 文档注释
文档注释使用 /**
开始,*/
结束。这种注释方式常用于生成API文档,可以包含对类、方法和属性的描述。
举个栗子:
/**
* 这是一个Javadoc注释
* 用于描述类、方法和属性的详细信息
*/
class MyClass {
/**
* 这是一个方法的注释示例
*
* @param name 用户的姓名
* @return 一个欢迎消息
*/
fun sayHello(name: String): String {
return "Hello, $name!"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
文档注释中的标签通常以 @
符号开头,用于描述函数、参数、返回值等详细信息。这些注释可以由文档生成工具(如KDoc)用来生成文档以供开发者参考。如果没有编程基础,上面的代码看起来比较懵逼,先不用过多理会。
简单的说,就是可以使用工具生成代码的说明文档,这些说明文档就是根据代码中的 文档注释
来生成的。
如果你已经学习过 Java 了,和 Java 一样,赶紧继续学吧。
# 2.3 打印语句
print()和println()
print() 和 println() 都是打印数据的,println() 表示打印完数据后,会换行,所以如果在 println() 后面继续打印的话,会在第二行打印。
举个栗子:
print("name:")
print("DouBi")
2
执行结果为:
name:Doubi
println("name:") // 打印完换行
print("DouBi")
2
执行结果为:
name:
Doubi
# 2.4 变量和常量
变量是在程序运行的时候存储数据用的,可以想象变量为一个盒子。如果我们要存储数据,就需要先定义变量,将数据存储的变量中。
# 1 定义变量
定义变量的语法 var 变量名: 数据类型 = 值
。
举个栗子:
fun main() {
// 定义整数变量,变量名称为age
var age: Int = 18
println(age) // 打印age
}
2
3
4
5
在上面,我们定义了一个整形的变量,变量名称为age,然后将整数18存储到变量age中,然后使用打印语句println()将变量打印到控制台,输出的结果是18。
同样,我们可以定义各种不同类型的变量,存储各种不同类型的数据:
fun main() {
// 定义字符串变量,变量名称为name,用来存储姓名
var name: String = "Doubi"
print(name) // 打印name
// 定义整数变量,变量名称为age
var age: Int = 18
// 定义长整数变量,使用 'L' 后缀表示长整数
var time: Long = 1234567890L
// 定义浮点数变量,需要添加 'F' 后缀表示单精度浮点数
var floatVariable: Float = 3.14F
// 定义浮点数变量,默认是双精度浮点数
var doubleVariable: Double = 2.71828
// 定义字符变量,字符类型只能表示单个字符,使用'括起来
var charVariable: Char = 'A'
// 定义布尔变量,只有两个值,true和false
var booleanVariable: Boolean = true
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
上面分别定义了不同数据类型的变量,将变量的值存储在不同的变量中。
和 Java 相比,这定义变量的方式看上去恶心至极,突然想吐,但其实数据类型是可以省略的。
举个栗子:
fun main() {
// 定义字符串变量,变量名称为name,用来存储姓名
var name = "Doubi"
print(name) // 打印name
// 定义整数变量,变量名称为age
var age = 18
// 定义长整数变量,使用 'L' 后缀表示长整数
var time = 1234567890L
}
2
3
4
5
6
7
8
9
10
11
省略了数据类型,这样看上去好多了。Kotlin 会根据后面的值自动推断变量的类型。
# 2 变量的操作
变量变量,是可以变化的量,所以我们可以随时修改变量的值。
例如:
fun main() {
var name = "DouBi" // 定义一个变量,用来表示姓名
name = "ShaBi" // 重新修改变量的值
// name = 123 // 错误, name是字符串类型的,无法赋值其他类型数据
println(name)
var height = 170
height = height + 10 // 将height在原来的基础上加 10
println(height);
}
2
3
4
5
6
7
8
9
10
需要注意,var name="DouBi";
已经确定了变量name的类型为字符串,无法使用 name = 123;
赋值其他类型数据。
# 3 常量
变量的值是可以变的,就像上面我们定义了变量,后面可以修改它的值。
常量就是值不能改变的。
在 Kotin 中,使用 val
或 const val
关键字来定义常量。
两者的区别在于:
val
关键字定义的常量是在第一次使用时初始化的,初始化后不可改变,又叫只读变量;const val
关键字定义的常量在程序运行之前,在程序编译时就已经被初始化了,永远不会改变,而且const val
只能定义在顶级作用域,或者在对象声明和伴生对象中使用(后面再讲解)。
举个栗子:
const val PI = 3.141592653 // 在全局使用const定义常量,程序运行之前就知道了值
//const val nowTime = System.currentTimeMillis() // 错误:System.currentTimeMillis()是获取当前时间,需要运行的时候才能确定值,所以不能赋值给const val常量
fun main() {
// const val PI = 3.141592653 // 错误:无法在这里使用const val定义常量,这在方法里,不是全局作用域
val PI2 = 3.141592653 // 使用val来定义常量
val nowTime: Long = System.currentTimeMillis() // val定义常量,需要等到程序第一次运行的时候才能知道值
}
2
3
4
5
6
7
8
从上面可以看到 const val
定义常量不能在方法中定义,需要在全局定义,并且值需要在运行前确定,而 val
定义常量没有这些要求。
常量和变量的类型都可以显式指定,也可以通过类型推断来自动推断。
通过查看字节码,反编译成对应的java代码,可以看到,val其实就是final修饰变量,const val其实就是static final修饰的变量。
# 2.5 数据类型与转换
# 1 Any
在前面我们介绍了很多的数据类型,这些数据类型有一个共同的父类型 Any
,Any
类型可以包含任何非空的Kotlin值。
举个栗子:
fun main() {
var name: Any = "Doubi"
var age: Any = 18
var value: Any = "Shabi"
value = 123 // 可以接收任何非空类型
}
2
3
4
5
6
7
在上面定义了 Any 类型的变量,Any 类型的变量因为是 Int 和 String 等类的父类,所以这些子类都是 Any 类。就像学生类和老师类都是人类,那么定义张三这个变量也肯定是人类。
所以在上面的代码中,字符串和整数都可以赋值给 Any 类型的变量,同时可以修改一个 Any 类型变量的值为不同的数据类型。
# 2 获取数据类型
在 Kotlin 中,可以使用 ::class
或 ::class.java
来获取一个变量的数据类型。
举个栗子:
使用 ::class
fun main() {
val num = 10
val str = "Hello, World!"
println(num::class.simpleName) // 输出: Int
println(str::class.simpleName) // 输出: String
}
2
3
4
5
6
7
首先使用 ::class
获取它的数据类型。然后可以使用 simpleName
属性来获取类型的简单名称。
# 3 Any转换为其他类型
相同数据类型的变量可以互相赋值,但是一个存储整数的 Any 类型的变量,如何赋值给另一个整形的变量呢?
fun main() {
var age: Any = 18
var value: Int = age // 报错,类型不匹配
}
2
3
4
那么如何将 Any 类型赋值给其他类型呢?
可以使用 as
关键字进行转换
举个栗子:
fun main() {
var age: Any = 18
var value: Int = age as Int // 将age转换为Int类型
}
2
3
4
上面使用 as
关键字将 Any
转换为 Int
类型。
这里如果 age 不是整数,那么转换为 Int 类型是会报错的,所以推荐在转换之前先进行类型的判断,判断 age 是否是整数,如果是整数在转换。
fun main() {
var age: Any = 18
if (age is Int) { // 先判断age是否是Int,如果不是Int,转换为Int会报错
var value: Int = age as Int // 将age转换为Int类型
}
}
2
3
4
5
6
7
上面用到了 if 判断,后面会讲解,age is Int
表示判断 age 是否是 Int 类型,如果是 Int 类型,则会执行后面 {}
中的代码。
# 4 数字类型之间的转换
Byte、Short、Int、Long、Float、Double、Char 之间如何转换呢?
他们都有提供下面的这些方法,可以转换为对应的类型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
2
3
4
5
6
7
举个栗子:
double 转 int
fun main() {
var a: Double = 12.89
var b: Int = a.toInt()
print(b) // 输出:12
}
2
3
4
5
double 类型转 int ,小数点后去掉,只保留整数部分。
如果要进行四舍五入,可以使用roundToInt()方法:
import kotlin.math.roundToInt // 需要引入roundToInt
fun main() {
var a: Double = 12.89
var b: Int = a.roundToInt()
print(b) // 13
}
2
3
4
5
6
7
# 5 String与数字相互转换
数字转换为String
直接使用数字的 toString()
方法将数字转换为字符串。
fun main() {
val intValue = 123
val intStr = intValue.toString() // 整数转换为字符串
val doubleValue = 3.14
val doubleStr = doubleValue.toString() // 浮点数转换为字符串
}
2
3
4
5
6
7
String转换为数字
String 类型提供了 toInt()
、 toDouble()
、 toFloat()
、 toLong()
方法,可以将字符串转换为数字。
举个栗子:
fun main() {
val str = "123"
val intValue = str.toInt()
val doubleValue = str.toDouble()
println(intValue)
println(doubleValue)
}
2
3
4
5
6
7
8
需要注意的是,如果字符串不能被解析为数字,则会报错,导致程序终止运行。
如果字符串不能解析为数字,不想程序报错,可以使用 toXXXOrNull() 方法。
举个栗子:
fun main() {
val str = "abc"
val intValue = str.toIntOrNull()
val doubleValue = str.toDoubleOrNull()
println(intValue) // 输出:null
println(doubleValue) // 输出:null
}
2
3
4
5
6
7
8
null
是一特殊的值,表示没有值,是空的,因为没有能把字符串转换为数字,所以得到的结果是空。
关于空(null)的问题,在后面空安全中再讲解。
# 2.6 标识符
什么是标识符?
标识符就是名字,例如变量的名字、方法的名字、类的名字。
起名字肯定会有限制,肯定不能 张Three 这样起名字,所以标识符也有限制。
# 1 标识符命名规则
标识符需要遵守一下规则:
- 只能是英文、数字、下划线,其他任何内容都不允许;
- 不能使用数字开头,可以英文或下划线开头;
var a = "Doubi" // 可以
var a_b = "Doubi" // 可以
var _a = "Doubi" // 可以
var a1 = "Doubi" // 可以
var a_b_a = "Doubi" // 可以
var 1 = "Doubi" // 错误
var 1_ = "Doubi" // 错误
var 1_a = "Doubi" // 错误
2
3
4
5
6
7
8
9
- 大小写敏感,大小写不同是不同的标识符;
var name = "Doubi"
var Name = "Doubi" // 和 name 是不同的标识符
2
- 不能使用关键字,关键字就是 Kotlin 中保留的一些单词,有特殊的用途,不能被用作标识符;
Kotlin 中常用的关键字和保留字如下:
as | break | class | continue | do |
---|---|---|---|---|
else | enum | false | field | finally |
for | fun | if | in | interface |
internal | is | lateinit | null | object |
package | return | super | this | throw |
true | try | typealias | val | var |
when | while | with | annotation | const |
crossinline | data | inline | infix | noinline |
open | operator | reified | sealed | suspend |
tailrec | vararg |
如果标识符中只是包含了关键字,是没有关系的。
这么多怎么记?不用记!不用记!不用记!后面每个关键字都会学到,自然知道每个关键字是做什么用的,不用记!
其实你如果名字不对,编译会报错,无法运行,自然就知道不对了。
# 2 变量命名规范
使用上面说的规则,我们可以定义变量名了。
但是为了优雅、统一、规范,我们在定义变量名时,还应该遵守以下规范,虽然你不遵守,没人开枪打死你,但是建议你遵守。
- 见名知意
看见一个变量名,就知道这个变量名是干嘛的。
var a = "Doubi" // 看到a,鬼知道a是干嘛的
var name = "Doubi" // 看到name,就知道这是个名字,简单明了
var personName = "Doubi" // 在确保明了的前提下,尽量减少长度,这个有点不够简洁
2
3
- 变量使用首字母小写的驼峰规则
var name = "Doubi"
var accountNumber = "Doubi"
2
- 常量名应该全部使用大写字母,如果常量名由多个单词组成,单词之间应该使用下划线
_
分隔
val MAX_VALUE = 123456
const val MIN_VALUE = 1
2
其他的命名,例如类名,学到的时候再讲解。
# 2.7 字符串
# 1 字符串定义方式
在上面我们使用双引号 " 来定义字符串,但其实字符串有2种不同的定义方式:
方式一:双引号定义法
val str1 = "我是一个字符串"
方式二:三引号定义法
使用3个引号定义字符串,还可以换行:
val str2: String = """
这是一个
多行字符串
"""
2
3
4
因为字符串是不可变的,一旦创建了就无法修改了,如果要修改,需要重新创建一个新的字符串。
所以在定义字符串的时候,建议使用 val
来定义。
# 2 转义字符
如果我们的字符串中包含引号,该怎么处理呢?
如果是用的是双引号定义的字符串,需要使用转义符 \ ,在引号前面加上 \ 表示后面的字符是普通字符串:
val str = "Hello \"Doubi\""
同样,如果你想在字符串中输出 \n ,你可能会这样写:
val str = "Hello \n Doubi";
println(str);
2
但运行完成结果却没有 \n ,因为 \n 是换行符,同样 \t 是制表符,如果想在字符串中输出 \t 或 \n 等特殊字符,也是需要对 斜杠 \ 进行转义:
val str = "Hello \\n Doubi";
println(str);
2
如果使用的是三引号定义法,那么就不用转义了,直接写就可以了。
val str: String = """Hello \n "Doubi""""
println(str) // 输出:Hello \n "Doubi"
2
但是如果其中有三个引号连起来,并且换行了那就不行了,也是需要使用 斜杠 \ 进行转义的。
val str1: String = """Hello \n "Doubi"""""" // 可以
val str2: String = """
Hello \n "Doubi""\""
"""
2
3
4
# 3 字符串拼接
# 方式一:使用字符串模板
Kotlin 支持字符串模板,你可以在字符串中使用 ${}
来嵌入变量或表达式的值。
举个栗子:
fun main() {
val name = "Doubi"
val age = 18
// 使用 ${} 进行字符串插值
val message = "我的名字是${name}, 今年${age}岁, 明年${age + 1}岁。"
print(message) // 输出:我的名字是Doubi, 今年18岁, 明年19岁。
}
2
3
4
5
6
7
8
可以看到在 ${} 括号内填写变量和执行任意的表达式。
如果直接拼接变量,还可以直接使用 $ :
fun main() {
val name = "Doubi"
val age = 18
val message1 = "我的名字是$name, 今年$age 岁, 明年${age + 1}岁。" // $age后面不能直接跟汉字
}
2
3
4
5
6
但是这里需要注意,$age
后面必须是符号或空格,不能是汉字,否则会被识别为变量名,比较坑爹。所以建议使用 ${变量}
的方式。
# 方式二:使用加号拼接
如果是字符串或字符串变量之间的拼接,直接使用 加号+ 拼接即可,例如:
val name = "zhangsan"
println("我的名字是" + name + ", 我是法外狂徒")
2
如果拼接的太多,就有点麻烦:
fun main() {
val name = "Doubi"
val age = 18
val message = "我的名字是" + name + ", 今年" + age + "岁, 明年" + (age + 1) +"岁。"
println(message)
}
2
3
4
5
6
7
所以建议使用字符串模板。
# 方式三:使用StringBuilder
如果要进行大量的字符串拼接,多次进行拼接,建议使用 StringBuilder
。
fun main() {
val stringBuilder = StringBuilder()
stringBuilder.append("Hello, ")
stringBuilder.append("world!")
stringBuilder.append("I ame ")
stringBuilder.append("Doubi")
val result = stringBuilder.toString() // 转换为字符串
println(result)
}
2
3
4
5
6
7
8
9
10
如果要进行频繁的拼接,使用StringBuilder
可以提高性能。
# 方式四:使用字符串插值函数
上面使用 StringBuilder
有点麻烦,Kotlin 还提供了 buildString
函数,可以更方便地构建字符串。
fun main() {
val result: String = buildString {
append("Hello, ")
append("world!")
append("I ame ")
append("Doubi")
}
println(result)
}
2
3
4
5
6
7
8
9
10
上面也是使用 StringBuilder
来构建的,使用方便,如果频繁拼接,推荐时候用这种方式。
# 4 判断字符串非空
可以使用 .isEmpty
和 .isNotEmpty
来判断字符串是否为空字符串。
空字符串就是没有内容的字符串。
举个栗子:
val str = ""
print(str.isEmpty()) // 输出: true
print(str.isNotEmpty()) // 输出: false
2
3
# 5 去掉字符串前后空格
用户输入的内容,前后有空格,可以使用 trim()
方法去掉前后的空格。
fun main() {
val str = " Hello, Kotlin "
val trimmedStr = str.trim()
println(trimmedStr) // 输出 "Hello, Kotlin"
}
2
3
4
5
注意:它返回一个新的字符串,原始字符串不会被修改。
如果想单独去掉前面或后面的空格,可以使用 trimStart()
和 trimEnd()
函数。
# 5 浮点数格式化
在输出浮点数的时候,我们可以指定输出的浮点数的精度。
使用字符串模板:
可以使用字符串模板来将浮点数格式化为字符串,并指定小数位数。以下是一个示例:
val number = 123.456789 val formattedString = "格式化后字符串: %.2f".format(number) println(formattedString) // 输出:格式化后字符串: 123.46
1
2
3上面的代码使用
%.2f
格式字符串来指定要显示的小数位数为 2。使用
String.format()
函数:还可以使用 Java 的
String.format()
函数,因为 Kotlin 可以与 Java 互操作。以下是示例代码:val number = 123.456789 val formattedString = String.format("格式化后:%.2f", number) println(formattedString) // 输出:格式化后:123.46
1
2
3使用
DecimalFormat
类:可以使用 Java 的
DecimalFormat
类,它提供了更多的格式化选项。首先,需要导入 Java 的DecimalFormat
类,然后使用它来格式化浮点数。import java.text.DecimalFormat fun main() { val number = 123.456789 val decimalFormat = DecimalFormat("格式化后:#.##") val formattedString = decimalFormat.format(number) println(formattedString) // 输出:格式化后:123.46 }
1
2
3
4
5
6
7
8
上述示例中,#.##
是格式化模式,它表示最多保留两位小数。可以根据需要调整格式化模式以获得所需的格式。
# 2.8 键盘输入
下面介绍一下如何接收键盘输入的内容。
在 Kotlin 中可以使用标准库中的 readLine()
函数来接收键盘输入。
举个栗子:
fun main() {
print("请输入一个字符串:")
val input = readLine()
println("您输入的字符串是:$input")
}
2
3
4
5
6
在上面的代码中,通过 readLine()
函数读取到键盘的输入,然后将读取的结果打印了出来。
运行后输入abc,执行结果:
请输入一个字符串:abc 您输入的字符串是:abc
# 2.9 扩展
Java 中有两种数据类型:基本数据类型、引用类型。
Kotlin 只提供了引用类型一种数据类型,但是出于性能的需要,Kotlin 编译器会在Java字节码中将一些数据类型(Byte、Short、Int、Long、Float、Double、Boolean、Char)改用基本数据类型。
← 01-Kotlin简介 03-运算与控制 →