Skip to content

Kotlin教程 - 2 基础语法

2.1 字面量

Kotlin 是一种静态类型的编程语言,它具有丰富的数据类型来表示不同种类的数据,先介绍一下 Kotlin 中基本的数据类型。

如果没学过其他语言,看数据类型可能比较懵逼,数据类型就是说数据有哪些类型,例如我们保存姓名,使用的的是字符串,也就是文本,但是保存年龄,是数字类型,金额可能使用的小数的类型,不同的数据会使用不同的数据类型来保存。

整数类型

下面这些都是整数:

kotlin
123
1234567
123456789101112L  		// 表示长整型,需要以L作为后缀
123L  								// 表示长整型
0x0F									// 十六进制形式整数
0b00001011						// 二进制形式的整数

Kotlin 中表示整数的类型有如下几种,他们的长度不同,所以可以表示的范围不同。

  • Byte:8 位有符号整数,取值范围为 -128 到 127。
  • Short:16 位有符号整数,取值范围为 -32768 到 32767。
  • Int:32 位有符号整数,通常是最常用的整数类型。
  • Long:64 位有符号整数,用于表示大整数值,例如时间戳(时间的毫秒数)。

上面这样在代码中直接书写的数据,被称为字面量。

如何使用数据类型,讲到变量的时候再讲解,不必着急。

浮点数类型

kotlin
123.5					// 默认就是双精度浮点数
123.5e10			// 科学计数法,表示123.5乘以10的10次方,平时用的不多
123.5f				// 单精度浮点数,后面需要加f后缀

Kotlin 中表示浮点数的类型有如下两种:

  • Float:32 位单精度浮点数(小数),用于表示小数,有约 6-7 位有效数字。
  • Double:64 位双精度浮点数(小数),用于表示双精度小数,有约 15-16 位有效数字。

为了便于阅读,还可以在字面量重添加下划线

kotlin
100_000_001
1234_5678_9012L
0xFF_EC_DE_5E					// 十六进制
0b11010010_01101011_10011100_10010011			// 二进制
123_4567_7890.123			// 双精度浮点数
123_4567.1f						// 单精度浮点数

字符类型

kotlin
'A'
'a'
'#'
'3' 	// 这不是整数,是一个字符
  • Char:16 位 Unicode 字符,用于表示单个字符,例如字母、数字或符号。

和Java不同,Kotlin中Char只能表示单个字符,不能和数字直接进行转换。

布尔类型

kotlin
true
false
  • Boolean:表示布尔值,只能取 truefalse

字符串类型

还有最常用的数据类型,就是字符串类型,用来存储文本数据。在Kotlin中,可以使用双引号 " 或三个双引号 """ 来定义字符串:

kotlin
"abc"

"""
你好,
China
"""
  • String:表示字符串,可以包含任何字符序列。

Kotlin中还有一些其他的数据类型,例如空类型、数组类型、自定义数据类型(类、枚举类等),这些后面在讲解,这里就不说了。

在 Java 中,数据类型分为基本数据类型和引用数据类型,但是在 Kotlin 中只有引用数据类型,只是Byte、Short、Int、Long、Float、Double、Boolean、Char ,在编译的时候,还是会编译为基本数据类型来处理。

2.2 注释

我们在学习任何语言,都会有注释,注释的作用就是向别人解释我们编写的代码的含义和逻辑,使代码有更好的可读性,注释不是程序,是不会被执行的

在Kotlin中注释分类三类,单行注释多行注释文档注释

1 单行注释

单行注释以 // 开头,// 号右边为注释内容。

例如:

kotlin
// 我是单行注释,打印Hello World
print("Hello World!")

注意:// 号和注释内容一般建议以一个空格隔开,这是代码规范,建议大家遵守。

单行注释一般用于对一行或一小部分代码进行解释。

2 多行注释

多行注释是以 /* */ 括起来,中间的内容为注释内容,注释内容可以换行。

dart
/*
我是多行注释,
可以跨越多行
*/
print("Hello World!")

多行注释一般用于描述一段代码或者变量的详细信息。

3 文档注释

文档注释使用 /** 开始,*/ 结束。这种注释方式常用于生成API文档,可以包含对类、方法和属性的描述。

举个栗子:

kotlin
/**  
 * 这是一个Javadoc注释  
 * 用于描述类、方法和属性的详细信息  
 */  
class MyClass {  
    /**
     * 这是一个方法的注释示例
     *
     * @param name 用户的姓名
     * @return 一个欢迎消息
     */
    fun sayHello(name: String): String {
        return "Hello, $name!"
    }
}

文档注释中的标签通常以 @ 符号开头,用于描述函数、参数、返回值等详细信息。这些注释可以由文档生成工具(如KDoc)用来生成文档以供开发者参考。如果没有编程基础,上面的代码看起来比较懵逼,先不用过多理会。

简单的说,就是可以使用工具生成代码的说明文档,这些说明文档就是根据代码中的 文档注释 来生成的。

如果你已经学习过 Java 了,和 Java 一样,赶紧继续学吧。

2.3 打印语句

print()和println()

print() 和 println() 都是打印数据的,println() 表示打印完数据后,会换行,所以如果在 println() 后面继续打印的话,会在第二行打印。

举个栗子:

kotlin
print("name:")
print("DouBi")

执行结果为:

name:Doubi

kotlin
println("name:")		// 打印完换行
print("DouBi")

执行结果为:

name:

Doubi

2.4 变量和常量

变量是在程序运行的时候存储数据用的,可以想象变量为一个盒子。如果我们要存储数据,就需要先定义变量,将数据存储的变量中。

1 定义变量

定义变量的语法 var 变量名: 数据类型 = 值

举个栗子:

kotlin
fun main() {
    // 定义整数变量,变量名称为age
    var age: Int = 18
    println(age)   // 打印age
}

在上面,我们定义了一个整形的变量,变量名称为age,然后将整数18存储到变量age中,然后使用打印语句println()将变量打印到控制台,输出的结果是18。

同样,我们可以定义各种不同类型的变量,存储各种不同类型的数据:

kotlin
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
}

上面分别定义了不同数据类型的变量,将变量的值存储在不同的变量中。

和 Java 相比,这定义变量的方式看上去恶心至极,突然想吐,但其实数据类型是可以省略的。

举个栗子:

kotlin
fun main() {
    // 定义字符串变量,变量名称为name,用来存储姓名
    var name = "Doubi"
    print(name)   // 打印name

    // 定义整数变量,变量名称为age
    var age = 18

    // 定义长整数变量,使用 'L' 后缀表示长整数
    var time = 1234567890L
}

省略了数据类型,这样看上去好多了。Kotlin 会根据后面的值自动推断变量的类型

2 变量的操作

变量变量,是可以变化的量,所以我们可以随时修改变量的值。

例如:

kotlin
fun main() {
    var name = "DouBi"     // 定义一个变量,用来表示姓名
    name = "ShaBi"         // 重新修改变量的值
    // name = 123 // 错误, name是字符串类型的,无法赋值其他类型数据
    println(name)
    
    var height = 170
    height = height + 10   // 将height在原来的基础上加 10
    println(height);
}

需要注意,var name="DouBi"; 已经确定了变量name的类型为字符串,无法使用 name = 123; 赋值其他类型数据。

3 常量

变量的值是可以变的,就像上面我们定义了变量,后面可以修改它的值。

常量就是值不能改变的。

在 Kotin 中,使用 valconst val 关键字来定义常量。

两者的区别在于:

  • val 关键字定义的常量是在第一次使用时初始化的,初始化后不可改变,又叫只读变量
  • const val 关键字定义的常量在程序运行之前,在程序编译时就已经被初始化了,永远不会改变,而且 const val 只能定义在顶级作用域,或者在对象声明和伴生对象中使用(后面再讲解)。

举个栗子:

kotlin
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定义常量,需要等到程序第一次运行的时候才能知道值
}

从上面可以看到 const val 定义常量不能在方法中定义,需要在全局定义,并且值需要在运行前确定,而 val 定义常量没有这些要求。

常量和变量的类型都可以显式指定,也可以通过类型推断来自动推断。

通过查看字节码,反编译成对应的java代码,可以看到,val其实就是final修饰变量,const val其实就是static final修饰的变量。

2.5 数据类型与转换

1 Any

在前面我们介绍了很多的数据类型,这些数据类型有一个共同的父类型 AnyAny 类型可以包含任何非空的Kotlin值。

举个栗子:

kotlin
fun main() {
    var name: Any = "Doubi"
    var age: Any = 18

    var value: Any = "Shabi"
    value = 123                // 可以接收任何非空类型
}

在上面定义了 Any 类型的变量,Any 类型的变量因为是 Int 和 String 等类的父类,所以这些子类都是 Any 类。就像学生类和老师类都是人类,那么定义张三这个变量也肯定是人类。

所以在上面的代码中,字符串和整数都可以赋值给 Any 类型的变量,同时可以修改一个 Any 类型变量的值为不同的数据类型。

2 获取数据类型

在 Kotlin 中,可以使用 ::class::class.java 来获取一个变量的数据类型。

举个栗子:

使用 ::class

kotlin
fun main() {
    val num = 10
    val str = "Hello, World!"

    println(num::class.simpleName)  // 输出: Int
    println(str::class.simpleName)  // 输出: String
}

首先使用 ::class 获取它的数据类型。然后可以使用 simpleName 属性来获取类型的简单名称。

3 Any转换为其他类型

相同数据类型的变量可以互相赋值,但是一个存储整数的 Any 类型的变量,如何赋值给另一个整形的变量呢?

kotlin
fun main() {
    var age: Any = 18
    var value: Int = age       // 报错,类型不匹配
}

那么如何将 Any 类型赋值给其他类型呢?

可以使用 as 关键字进行转换

举个栗子:

kotlin
fun main() {
    var age: Any = 18
    var value: Int = age as Int     // 将age转换为Int类型
}

上面使用 as 关键字将 Any 转换为 Int 类型。

这里如果 age 不是整数,那么转换为 Int 类型是会报错的,所以推荐在转换之前先进行类型的判断,判断 age 是否是整数,如果是整数在转换。

kotlin
fun main() {
    var age: Any = 18

    if (age is Int) {       // 先判断age是否是Int,如果不是Int,转换为Int会报错
        var value: Int = age as Int     // 将age转换为Int类型
    }
}

上面用到了 if 判断,后面会讲解,age is Int 表示判断 age 是否是 Int 类型,如果是 Int 类型,则会执行后面 {} 中的代码。

4 数字类型之间的转换

Byte、Short、Int、Long、Float、Double、Char 之间如何转换呢?

他们都有提供下面的这些方法,可以转换为对应的类型:

kotlin
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char

举个栗子:

double 转 int

kotlin
fun main() {
    var a: Double = 12.89
    var b: Int = a.toInt()
    print(b)		// 输出:12
}

double 类型转 int ,小数点后去掉,只保留整数部分。

如果要进行四舍五入,可以使用roundToInt()方法:

kotlin
import kotlin.math.roundToInt		// 需要引入roundToInt

fun main() {
    var a: Double = 12.89
    var b: Int = a.roundToInt()
    print(b)    // 13
}

5 String与数字相互转换

数字转换为String

直接使用数字的 toString() 方法将数字转换为字符串。

kotlin
fun main() {
    val intValue = 123
    val intStr = intValue.toString()        // 整数转换为字符串
    
    val doubleValue = 3.14
    val doubleStr = doubleValue.toString()  // 浮点数转换为字符串
}

String转换为数字

String 类型提供了 toInt()toDouble() toFloat() toLong() 方法,可以将字符串转换为数字。

举个栗子:

kotlin
fun main() {
    val str = "123"
    val intValue = str.toInt()
    val doubleValue = str.toDouble()

    println(intValue)
    println(doubleValue)
}

需要注意的是,如果字符串不能被解析为数字,则会报错,导致程序终止运行。

如果字符串不能解析为数字,不想程序报错,可以使用 toXXXOrNull() 方法。

举个栗子:

kotlin
fun main() {
    val str = "abc"
    val intValue = str.toIntOrNull()
    val doubleValue = str.toDoubleOrNull()

    println(intValue)			// 输出:null
    println(doubleValue)	// 输出:null
}

null 是一特殊的值,表示没有值,是空的,因为没有能把字符串转换为数字,所以得到的结果是空。

关于空(null)的问题,在后面空安全中再讲解。

2.6 标识符

什么是标识符?

标识符就是名字,例如变量的名字、方法的名字、类的名字。

起名字肯定会有限制,肯定不能 张Three 这样起名字,所以标识符也有限制。

1 标识符命名规则

标识符需要遵守一下规则:

  1. 只能是英文、数字、下划线,其他任何内容都不允许;
  2. 不能使用数字开头,可以英文或下划线开头;
kotlin
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"				// 错误
  1. 大小写敏感,大小写不同是不同的标识符;
kotlin
var name = "Doubi"					
var Name = "Doubi"				    // 和 name 是不同的标识符
  1. 不能使用关键字,关键字就是 Kotlin 中保留的一些单词,有特殊的用途,不能被用作标识符;

Kotlin 中常用的关键字和保留字如下:

asbreakclasscontinuedo
elseenumfalsefieldfinally
forfunifininterface
internalislateinitnullobject
packagereturnsuperthisthrow
truetrytypealiasvalvar
whenwhilewithannotationconst
crossinlinedatainlineinfixnoinline
openoperatorreifiedsealedsuspend
tailrecvararg

如果标识符中只是包含了关键字,是没有关系的。

这么多怎么记?不用记!不用记!不用记!后面每个关键字都会学到,自然知道每个关键字是做什么用的,不用记!

其实你如果名字不对,编译会报错,无法运行,自然就知道不对了。

2 变量命名规范

使用上面说的规则,我们可以定义变量名了。

但是为了优雅、统一、规范,我们在定义变量名时,还应该遵守以下规范,虽然你不遵守,没人开枪打死你,但是建议你遵守。

  1. 见名知意

看见一个变量名,就知道这个变量名是干嘛的。

kotlin
var a = "Doubi"									// 看到a,鬼知道a是干嘛的
var name = "Doubi"							// 看到name,就知道这是个名字,简单明了
var personName = "Doubi"		    // 在确保明了的前提下,尽量减少长度,这个有点不够简洁
  1. 变量使用首字母小写的驼峰规则
kotlin
var name = "Doubi"
var accountNumber = "Doubi"
  1. 常量名应该全部使用大写字母,如果常量名由多个单词组成,单词之间应该使用下划线 _ 分隔
kotlin
val MAX_VALUE = 123456
const val MIN_VALUE = 1

其他的命名,例如类名,学到的时候再讲解。

2.7 字符串

1 字符串定义方式

在上面我们使用双引号 " 来定义字符串,但其实字符串有2种不同的定义方式:

方式一:双引号定义法

kotlin
val str1 = "我是一个字符串"

方式二:三引号定义法

使用3个引号定义字符串,还可以换行:

kotlin
val str2: String = """
    这是一个
    多行字符串
    """

因为字符串是不可变的,一旦创建了就无法修改了,如果要修改,需要重新创建一个新的字符串。

所以在定义字符串的时候,建议使用 val 来定义。

2 转义字符

如果我们的字符串中包含引号,该怎么处理呢?

如果是用的是双引号定义的字符串,需要使用转义符 \ ,在引号前面加上 \ 表示后面的字符是普通字符串:

kotlin
val str = "Hello \"Doubi\""

同样,如果你想在字符串中输出 \n ,你可能会这样写:

kotlin
val str = "Hello \n Doubi";
println(str);

但运行完成结果却没有 \n ,因为 \n 是换行符,同样 \t 是制表符,如果想在字符串中输出 \t\n 等特殊字符,也是需要对 斜杠 \ 进行转义:

kotlin
val str = "Hello \\n Doubi";
println(str);

如果使用的是三引号定义法,那么就不用转义了,直接写就可以了。

kotlin
val str: String = """Hello \n "Doubi""""
println(str)		// 输出:Hello \n "Doubi"

但是如果其中有三个引号连起来,并且换行了那就不行了,也是需要使用 斜杠 \ 进行转义的。

kotlin
val str1: String = """Hello \n "Doubi""""""           // 可以
val str2: String = """
    Hello \n "Doubi""\""
"""

3 字符串拼接

方式一:使用字符串模板

Kotlin 支持字符串模板,你可以在字符串中使用 ${} 来嵌入变量或表达式的值。

举个栗子:

kotlin
fun main() {
    val name = "Doubi"
    val age = 18

    // 使用 ${} 进行字符串插值
    val message = "我的名字是${name}, 今年${age}岁, 明年${age + 1}岁。"
    print(message) // 输出:我的名字是Doubi, 今年18岁, 明年19岁。
}

可以看到在 ${} 括号内填写变量和执行任意的表达式

如果直接拼接变量,还可以直接使用 $

kotlin
fun main() {
    val name = "Doubi"
    val age = 18

    val message1 = "我的名字是$name, 今年$age 岁, 明年${age + 1}岁。"		// $age后面不能直接跟汉字
}

但是这里需要注意,$age 后面必须是符号或空格,不能是汉字,否则会被识别为变量名,比较坑爹。所以建议使用 ${变量} 的方式

方式二:使用加号拼接

如果是字符串或字符串变量之间的拼接,直接使用 加号+ 拼接即可,例如:

kotlin
val name = "zhangsan"
println("我的名字是" + name + ", 我是法外狂徒")

如果拼接的太多,就有点麻烦:

kotlin
fun main() {
    val name = "Doubi"
    val age = 18

    val message = "我的名字是" + name + ", 今年" + age + "岁, 明年" + (age + 1) +"岁。"
    println(message)
}

所以建议使用字符串模板。

方式三:使用StringBuilder

如果要进行大量的字符串拼接,多次进行拼接,建议使用 StringBuilder

kotlin
fun main() {
    val stringBuilder = StringBuilder()
    stringBuilder.append("Hello, ")
    stringBuilder.append("world!")
    stringBuilder.append("I ame ")
    stringBuilder.append("Doubi")

    val result = stringBuilder.toString()		// 转换为字符串
    println(result)
}

如果要进行频繁的拼接,使用StringBuilder 可以提高性能。

方式四:使用字符串插值函数

上面使用 StringBuilder 有点麻烦,Kotlin 还提供了 buildString 函数,可以更方便地构建字符串。

kotlin
fun main() {
    val result: String = buildString {
        append("Hello, ")
        append("world!")
        append("I ame ")
        append("Doubi")
    }

    println(result)
}

上面也是使用 StringBuilder 来构建的,使用方便,如果频繁拼接,推荐时候用这种方式。

4 判断字符串非空

可以使用 .isEmpty.isNotEmpty 来判断字符串是否为空字符串。

空字符串就是没有内容的字符串。

举个栗子:

kotlin
val str = ""
print(str.isEmpty())        // 输出: true
print(str.isNotEmpty())     // 输出: false

5 去掉字符串前后空格

用户输入的内容,前后有空格,可以使用 trim() 方法去掉前后的空格。

kotlin
fun main() {
    val str = "   Hello, Kotlin   "
    val trimmedStr = str.trim()
    println(trimmedStr) // 输出 "Hello, Kotlin"
}

注意:它返回一个新的字符串,原始字符串不会被修改。

如果想单独去掉前面或后面的空格,可以使用 trimStart()trimEnd() 函数。

5 浮点数格式化

在输出浮点数的时候,我们可以指定输出的浮点数的精度。

  1. 使用字符串模板

    可以使用字符串模板来将浮点数格式化为字符串,并指定小数位数。以下是一个示例:

    kotlin
    val number = 123.456789
    val formattedString = "格式化后字符串: %.2f".format(number)
    println(formattedString) // 输出:格式化后字符串: 123.46

    上面的代码使用 %.2f 格式字符串来指定要显示的小数位数为 2。

  2. 使用 String.format() 函数

    还可以使用 Java 的 String.format() 函数,因为 Kotlin 可以与 Java 互操作。以下是示例代码:

    kotlin
    val number = 123.456789
    val formattedString = String.format("格式化后:%.2f", number)
    println(formattedString) // 输出:格式化后:123.46
  3. 使用 DecimalFormat

    可以使用 Java 的 DecimalFormat 类,它提供了更多的格式化选项。首先,需要导入 Java 的 DecimalFormat 类,然后使用它来格式化浮点数。

    kotlin
    import java.text.DecimalFormat
    
    fun main() {
        val number = 123.456789
        val decimalFormat = DecimalFormat("格式化后:#.##")
        val formattedString = decimalFormat.format(number)
        println(formattedString) // 输出:格式化后:123.46
    }

上述示例中,#.## 是格式化模式,它表示最多保留两位小数。可以根据需要调整格式化模式以获得所需的格式。

2.8 键盘输入

下面介绍一下如何接收键盘输入的内容。

在 Kotlin 中可以使用标准库中的 readLine() 函数来接收键盘输入。

举个栗子:

kotlin
fun main() {
    print("请输入一个字符串:")
    val input = readLine()

    println("您输入的字符串是:$input")
}

在上面的代码中,通过 readLine() 函数读取到键盘的输入,然后将读取的结果打印了出来。

运行后输入abc,执行结果:

请输入一个字符串:abc 您输入的字符串是:abc

2.9 扩展

Java 中有两种数据类型:基本数据类型、引用类型。

Kotlin 只提供了引用类型一种数据类型,但是出于性能的需要,Kotlin 编译器会在Java字节码中将一些数据类型(Byte、Short、Int、Long、Float、Double、Boolean、Char)改用基本数据类型。