以下均来自官方文档:
一、整数类型
1、kotlin中内置的整数类型,有四种不同大小的类型:
类型 | 存储大小(比特数) | 最小值 | 最大值 |
---|---|---|---|
Byte | 8 | -128 | 127 |
Short | 16 | -32768 | 32767 |
Int | 32 | -2,147,483,648 (-231) | 2,147,483,647 (231 - 1) |
Long | 64 | -9,223,372,036,854,775,808 (-263) | 9,223,372,036,854,775,807 (263 - 1) |
注:
当初始化一个没有显示指定类型的变量时,编译器会自动推断为自Int起足以表示该值的最小类型。意思是:如果不超过Int范围,那么类型为Int,如果超过了,那么类型是Long。如果指定值类型为Long类型,请给该值追加后缀L。如果显示的指定类型,会触发编译器检测该值是否超过指定类型的范围。
2、代码演示
// val或var来定义a = 1,打印类型,默认为java的int类型
fun main() {
val a = 1
// Kotlin的类型信息在运行时是基于JVM的,所以可以利用Java的反射API来获取类型信息
println("类型为: ${a.javaClass.simpleName}") // 类型为: int
}
输出结果:
// 下边打印输出类型为long类型,因为类型推断超出Int类型,所以输出long类型
fun main() {
val a = 10000000000000
println("类型为: ${a.javaClass.simpleName}") // 类型为: long
}
输出结果:
显示指定Long类型
// 值后边加上L,来显示指定Long类型,或者 定义Long类型
fun main() {
val a = 100L
val b: Long = 100
println("类型为: ${a.javaClass.simpleName}") // 类型为: long
println("类型为: ${b.javaClass.simpleName}") // 类型为: long
}
二、浮点类型
1、kotlin中内置的浮点类型:单精度Float与双精度Double类型
这两个类型的大小不同,并为两种不同精度的浮点数提供存储:
类型 | 大小(比特数) | 有效数字比特数 | 指数比特数 | 十进制位数 |
---|---|---|---|---|
Float | 32 | 24 | 8 | 6-7 |
Double | 64 | 53 | 11 | 15-16 |
可以使用带小数部分的数字初始化Double与Float变量。小数部分与整数部分之间用点 . 分割,对于以小数初始化的变量,编译器会自动推断为Double类型:
fun main() {
val a = 100.1
println("类型为: ${a.javaClass.simpleName}") // 类型为: double
}
如果需要将一个值显式指定为Float类型,请添加 f 或 F 后缀。如果值包含多于6到7位十进制数,那么会将其四舍五入:
// 实际测试Float类型多于4到5位十进制数,都会四舍五入,不知为啥!
fun main() {
val a = 100.31415966 // 默认推断Double类型
val b = 100.3141596f // 指定Float类型
println("a为: ${a}") // a为: 100.31415966
println("b为: ${b}") // b为: 100.31416
}
三、数字的装箱拆箱
1、在jvm平台,数字的存储为原生类型 int、double等。有例外的情况是:当创建可空数字引用如:泛型、Int? ···。在这些场景中,数字会装箱为JAVA类 Integer、Double等。
解释一下:在java世界里,有两种存放数字的方式:
(1)直接存储数字(原生类型):
就像你在口袋里直接放了几块糖,你知道那是几块,可以直接用。在Java中,int
、double
这些就是这种类型的糖,它们直接存数字,速度快,效率高。比如,你有个int num = 5;
,这里的num
就像你口袋里的5块糖,很直接。
(2)把数字放进盒子中,在存储(装箱类型):
有时候,你可能想要更灵活一点,比如说,你的糖可能会没有(因为没买或者吃完了),这时候你就需要一个盒子来帮助你表示“有糖”或“没糖”。在Java中,如果你用Integer
、Double
这样的类型,就像是给糖准备了个盒子。当你写Integer num = null;
或用在泛型、可空类型如Int?
时,就相当于,这个盒子里可能有糖(具体的数字),也可能什么都没有(null)。
装箱就是把简单直接的糖(原生类型)包装进一个盒子(变成对象,如Integer
),这样可以做更多事情,比如表示“无糖状态”,但相对的,操作起来比直接拿糖要麻烦一点,因为每次要用糖时,都要从盒子里拿出来(拆箱)。
原生类型(如int
、double
)直接存储数值,效率高;而像Integer
、Double
这样的装箱类型,则是把数值包装成对象,可以表示额外的“无值”状态,但在使用时涉及到自动装箱和拆箱,稍微复杂一些。
2、代码演示
fun main() {
val a: Int = 100
// 装箱,赋值为可空的b
val b: Int? = a
// 装箱,赋值为可空的c
val c: Int? = a
val d: Int = 10000
// 装箱,赋值为可空的e
val e: Int? = d
// 装箱,赋值为可空的f
val f: Int? = d
println(b === c) // true
println(e === f) // false
}
解释:
上边代码演示了装箱(boxing)和常量池(constant pool)的概念,这是理解打印结果差异的关键。
在Kotlin中,当一个原始类型(如Int
)被赋值给一个可空类型(如Int?
),这个过程被称为装箱,即原始类型值被封装成一个对象。但是,为了优化性能,Kotlin(以及Java)会对特定范围内的Int
值(通常是-128到127)使用缓存,这意味着在这个范围内的值在装箱时会复用同一个对象。这就是所谓的享元模式(Flyweight Pattern),可以减少内存使用并提高效率。
分析代码:
-
对于变量
a
,它的值是100,处于上述的缓存范围(-128到127)内。所以,当a
被装箱赋值给b
和c
时,这两个变量实际上引用的是同一个缓存中的Integer
对象。因此,b === c
比较的是两个对象的引用是否相同,结果为true
,表示它们确实是同一个对象。 -
变量
d
的值是10000,超出了常量池的缓存范围。因此,当d
被装箱为e和f
时,会为每个变量创建一个新的Integer
对象,即使它们的值相同。这意味着e
和f
是两个不同的对象,即使它们的数值相等。因此,e=== f
比较的是不同对象的引用,结果为false
。
总结:
打印结果的不同是因为值为100的Int
对象在装箱时被缓存并复用,而值为10000的Int
对象由于超出缓存范围,每次装箱都会创建新的对象实例。这就解释了为什么第一个比较结果为true
而第二个为false
。
再来看一段代码:
fun main() {
val a: Int = 10000
// 装箱,赋值为可空的b
val b: Int? = a
// 装箱,赋值为可空的c
val c: Int? = a
println(b == c) // true
}
为什么上边代码打印结果为true?
解释:
b == c
打印出true
的原因在于这里使用的是==
操作符来进行比较,而不是===
。在Kotlin中,==
用于比较两个对象的内容(值)是否相等,而===
用于比较两个引用是否指向同一个对象(即它们是否完全相同)。
分析代码:
当比较的是两个装箱的Int?
类型变量(b
和c
)时,==
操作符会触发自动拆箱(unboxing),并将它们的原始Int
值进行比较。在这个例子中,b
和c
虽然是两个不同的对象(因为它们是分别装箱得到的),但它们的内部整数值都是10000,所以b == c
的结果为true
。
总结:
b == c
是比较的两个变量的值是否相等,而不是它们是否是同一个对象,所以结果是true
。
四、类型转换
1、数字类型的互转
toByte(): 转Byte类型
toShort(): 转Short类型
toInt(): 转Int类型
toLong(): 转Long类型
toFloat(): 转Float类型
toDouble(): 转Double类型
fun main() {
val a = 1 // 默认会推断为Int类型
println(a.toByte()) // 1
println(a.toInt()) // 1
println(a.toFloat()) // 1.0
println(a.toDouble()) // 1.0
println(a.toLong()) // 1
println(a.toShort()) // 1
}
五、大小比较及区间检测
1、kotlin中大小比较跟java一样
- 相等性检测:
a == b
与a != b
- 比较操作符:
a < b
、a > b
、a <= b
、a >= b
- 区间实例以及区间检测:
a..b
、x in a..b
、x !in a..b
代码演示(.. 区间):
// a..b 表示从a到b这个区间
fun main() {
for (i in 1..100){
println(i) // 会打印1-100 的数字
}
}