Kotlin 基本数据类型

2020-04-11  本文已影响0人  bruce1990

基本类型

在Kotlin中,所有东西都是对象,在这个意义上讲我们可以在任何变量上调用成员函数与属 性。 一些类型可以有特殊的内部表示——例如,数字、字符以及布尔值可以在运行时表示为 原生类型值,但是对于用户来说,它们看起来就像普通的类。在本节中,我们会描述Kotlin 中使用的基本类型:数字、字符、布尔值、数组与字符串。

数字

Kotlin 的基本数值类型包括 Byte、Short、Int、Long、Float、Double 等和java类似只不过第一个字符是大写。不同于 Java 的是,字符不属于数值类型,是一个独立的数据类型。


图片.png
图片.png

字面常量

下面是所有类型的字面常量:

十进制:123
长整型以大写的 L 结尾:123L
16 进制以 0x 开头:0x0F
2 进制以 0b 开头:0b00001011
注意:8进制不支持

Kotlin 同时也支持传统符号表示的浮点数值:

Doubles 默认写法: 123.5, 123.5e10
Floats 使用 f 或者 F 后缀:123.5f

你可以使用下划线使数字常量更易读:

val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

案例

//数字
    val i = 1 //自动推导类型Int
    val b:Byte=127 //显示声明为Byte类型
    val s:Short=32767 //显示声明为Short类型
    val l=10L //后缀添加L显示指定为Long类型
    println("Byte类型=$b,Short类型=$s,Int类型=$i,Long类型=$l")
    val pi=3.14 //默认自动推导为Double类型
    val f=12345.141592625f //添加f或者F显示指定为Float
    println("Double类型=$pi,Float类型=$f")

输出
Byte类型=127,Short类型=32767,Int类型=1,Long类型=10
Double类型=3.14,Float类型=12345.142

比较两个数字
Kotlin 中没有基础数据类型,只有封装的数字类型,你每定义的一个变量,其实 Kotlin 帮你封装了一个对象,这样可以保证不会出现空指针。数字类型也一样,所有在比较两个数字的时候,就有比较数据大小和比较两个对象是否相同的区别了。
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小。

    //比较数字
    val a: Int = 10000
    println(a === a) // true,值相等,对象地址相等  三个=号比较地址
    val i = 1
    val j = 1
    println(i == j)//数值相等
    //使用了可空应用会导致装箱,经过了装箱,创建了两个不同的对象
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a
    //虽然经过了装箱,但是值是相等的,都是10000
    println(boxedA === anotherBoxedA) //  false,值相等,对象地址不一样
    println(boxedA == anotherBoxedA) // true,值相等

浮点数比较
相等性检测: a== b与a!=b
比较操作符: a<b 、a >b、a<= b、a>=b
和java是一样的不同的是kotlin里面的区间
区间实例以及区间检测:a..b 、x in a..b、x !in a..b

类型转换

由于不同的表示方式,较小类型并不是较大类型的子类型,较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。比如这样会编译错误

val b: Byte = 1    //   OK, 字面值是静态检测的               
val i: Int = b    //    错误 

我们可以显式转换来拓宽数字

    val b: Byte = 1 // OK, 字面值是静态检测的
    val i: Int = b.toInt() // OK
    print(i)

每种数据类型都有下面的这些方法,可以转化为其它的类型:

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

有些情况下也是可以使用自动类型转化的,前提是可以根据上下文环境推断出正确的数据类型而且数学操作符会做相应的重载。例如下面是正确的:
val l = 1L + 3 // Long + Int => Long

位运算

这是完整的位运算列表(只用于Int与Long):

shl(bits) – 左移位 (Java’s <<)
shr(bits) – 右移位 (Java’s >>)
ushr(bits) – 无符号右移位 (Java’s >>>)
and(bits) – 与
or(bits) – 或
xor(bits) – 异或
inv() – 反向

    val i = 8
    println("左移两位——${i shl 2}")//乘以2的2次幂
    println("右移两位——${i shr 2}")//除以2的2次幂
    val a = 1
    val b = 0
    println("与运算——${a and b}")
    println("或运算——${a or b}")
    println("异或运算——${a xor b}")
    println("反向运算——${b.inv()}")//没看懂这个

字符

和 Java 不一样,Kotlin 中的 Char 不能直接和数字操作,Char 必需是单引号 ' 包含起来的。比如普通字符 '0','a'。

fun check(c: Char) {
    if (c == 1) { // 错误:类型不兼容
        // ……
    }
}

字符字面值用单引号括起来: '1'。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t、 \b、\n、\r、'、"、\ 和 $。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'。

我们可以显式把字符转换为 Int 数字:

fun decimalDigitValue(c: Char): Int {
    if (c !in '0'..'9')
        throw IllegalArgumentException("Out of range")
    return c.toInt() - '0'.toInt() // 显式转换为数字
}

当需要可空引用时,像数字、字符会被装箱。装箱操作不会保留同一性。

布尔 (这个哥们基本上和java一致)

布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。
内置的布尔运算有: || 逻辑或、&&逻辑与、! 逻辑非

数组

数组用类 Array 实现,并且还有一个 size 属性及 get 和 set 方法,由于使用 [] 重载了 get 和 set 方法,所以我们可以通过下标很方便的获取或者设置数组对应位置的值。
数组的创建两种方式:一种是使用函数arrayOf();另外一种是使用工厂函数。如下所示,我们分别是两种方式创建了两个数组:

    //数组
    val intArr = arrayOf(1, 2, 3)//用arrayOf()创建数组
    for (i in intArr) {
        println(i)
    }
    val array = Array(3) { i -> (i) }//工厂函数创建
    array.forEach { println(it) }
    val strArr = arrayOfNulls<String>(5)//第三种采用创建指定长度的空数组  如果没有设置值将会为null
    strArr[0] = "H"
    strArr[1] = "e"
    strArr[2] = "l"
    strArr[3] = "l"
    strArr[4] = "o"
    strArr.forEach { print(it) }

如上所述,[] 运算符代表调用成员函数 get() 和 set()。
注意: 与 Java 不同的是,Kotlin 中数组是不型变的(invariant)。

原生类型数组

除了类Array,还有ByteArray, ShortArray, IntArray,用来表示各个类型的数组,省去了装箱操作,因此效率更高,其用法同Array一样:

    val x: IntArray = intArrayOf(1, 2, 3) //IntArray可以省略
    x[0] = x[1] + x[2]
    x.forEach {println(it) }
    val arr = IntArray(5)// 大小为 5、值为    [0, 0,  0,  0,  0]  的整型数组
    arr.forEach { println(it) }

    //  例如:用常量初始化数组中的值
    //  大小为 5、值为    [42,42, 42, 42, 42] 的整型数组
    val arr2 = IntArray(5) { 42 }
    arr2.forEach { println(it) }
    //  例如:使用   lambda  表达式初始化数组中的值 //  大小为 5、值为    [0, 1,  2,  3,  4]  的整型数组(值初始化为其索引值)
    val arr3 = IntArray(5) { it * 1 }
    arr3.forEach { println(it) }

字符串

和 Java 一样,String 是不可变的。方括号 [] 语法可以很方便的获取字符串中的某个字符,也可以通过 for 循环来遍历:

    val str = "Hello"
    for (c in str) {//遍历字符串里的字符
        println(c)
    }
    print(str[0])//直接用[]获取字符

Kotlin 支持三个引号 """ 扩起来的字符串,支持多行字符串,比如:

fun main(args: Array<String>) {
    val text = """
    多行字符串
    多行字符串
    """
    println(text)   // 输出有一些前置空格
}

String 可以通过 trimMargin() 方法来删除多余的空白。

    val text = """
        |大家好!
        |我是渣渣辉
        |回收装备
        |就玩贪玩蓝月
        """.trimMargin()//trimMargin()去除多余空白 默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")。
    println(text)
    val s = " 带空格的 字符 "
    println(s.trim())//这个和java的一样

字符串模板

字符串可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。 模板表达式以美元符($)开头,由一个简单的名字构成:

fun main(args: Array<String>) {
    val i = 10
    val s = "i = $i" // 求值结果为 "i = 10"
    println(s)
}

或者用花括号扩起来的任意表达式:

fun main(args: Array<String>) {
    val s = "runoob"
    val str = "$s.length is ${s.length}" // 求值结果为 "runoob.length is 6"
    println(str)
}

原生字符串和转义字符串内部都支持模板。 如果你需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义),你可以用下列语法:

fun main(args: Array<String>) {
    val price = """
    ${'$'}9.99
    """
    println(price)  // 求值结果为 $9.99
}

其实没什么区别,直接写$也是一样的

笔记(重点)

1.关于装箱的问题

fun main() {
    val a: Int = 100
    println(a === a) // true,值相等,对象地址相等

//经过了装箱,创建了两个不同的对象
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

//虽然经过了装箱,但是值是相等的,都是100
    println(boxedA === anotherBoxedA) //  true,值相等,128 之前对象地址一样  大于128 地址就不一样了
    println(boxedA == anotherBoxedA) // true,值相等
}

这里我把 a 的值换成 100,这里应该跟 Java 中是一样的,在范围是 [-128, 127] 之间并不会创建新的对象,比较输出的都是 true,从 128 开始,比较的结果才为 false。
结论: [-128, 127] 之外如果使用了可空标识就会装箱产生一个新的对象 (地址不再相同)

// Int? Int? Int?
val a1: Int? = 128
var boxedA: Int? = a1
var boxedB: Int? = a1
log(" boxedA === boxedB ? " + (boxedA === boxedB))//true
log(" boxedA == boxedB ? " + (boxedA == boxedB))//true

// Int Int? Int?
val a2: Int = 128
boxedA = a2
boxedB = a2
log(" boxedA === boxedB ? " + (boxedA === boxedB))//false
//值相等
log(" boxedA == boxedB ? " + (boxedA == boxedB))//true

// Int? Int? Int?
val a3: Int? = 127
boxedA = a3
boxedB = a3
log(" boxedA === boxedB ? " + (boxedA === boxedB))//true
//值相等
log(" boxedA == boxedB ? " + (boxedA == boxedB))//true

// Int Int? Int?
val a4: Int = 127
boxedA = a4
boxedB = a4
log(" boxedA === boxedB ? " + (boxedA === boxedB))//true
//值相等
log(" boxedA == boxedB ? " + (boxedA == boxedB))//true



// Int Int Int
val a5: Int = 128
val boxedA2: Int = a5
val boxedB2: Int = a5

log(" boxedA === boxedB ? " + (boxedA2 === boxedB2))//true
//值相等
log(" boxedA == boxedB ? " + (boxedA2 == boxedB2))//true

想象一下,假设有一堆货物(初始值)如果货物比较少(在[-128, 127] 之间。java里面就是自动装箱,可以理解为货物本身自带包装盒)那么把这些东西无论给甲还是给乙都不需要额外的箱子来装。所以甲乙手上的货物是一致的,二者相等。如果货物比较多([-128, 127]之外,多余一个包装盒。或者只多出一点,一个包装盒+一点散货)手拿不住了,需要拿箱子装了(装箱操作)那么甲需要一个箱子,乙需要一个箱子,手上的两个箱子是不同的(地址不同了)但是里面的货物还是一样的(值相等)

上一篇下一篇

猜你喜欢

热点阅读