玩转Kotlin之数据类型

目录

前言

一、Kotlin简介

二、Kotlin数据类型

2-1、Boolean类型

2-2、Number类型

2-3、Char类型

2-4、拆箱装箱

2-5、基础数据类型转换

2-6、字符串

三、类和对象

3-1、什么是类?

3-2、什么是对象?

3-3、类和对象的关系

3-4、类的继承

3-5、代码示例

四、空类型和智能类型转换

4-1、空类型

4-2、类型转换

五、包(Package)

六、区间(Range)

七、数组


前言

今天来学习一下Kotlin这门程序语言,对于一门语言它肯定不是一天能学完的,这个过程会持续一段时间。其实学习程序语言跟我们学习其他语言都是类似的,我们小时候学习中文或者英文的时候是怎样学习的还记得吗?是不是首先学习字母,接着学习单词,然后是语法,最后才是写作文,程序语言的学习流程也是一样的,开始学习基本的数据类型,接着学习程序的控制语句,然后学习函数,最后综合起来写一段业务代码。所以这是一个循序渐进的过程,希望都能做到有始有终!

一、Kotlin简介

1、Kotlin是一门可以运行在Java虚拟机、Android、浏览器上的静态语言,它与Java 100%兼容,Kotlin除了自己的标准库之外,大多仍然使用经典的Java集合框架。Kotlin V1.0于2016年2月15日发布,是第一个官方稳定版本,JetBrains从该版本开始长期向后兼容,在Google I/O 2017中,Google宣布Kotlin成为Android的官方开发语言。

2、入门心法之《Hello World》

一套从天而降的掌法那是相当厉害啦,但是一口吃不成个胖子,除非你本来就胖,那我们还是得从基本功练起,先来看个入门内功心法Kotlin——Hello World!在写代码之前还得说两句,因为我们是在学语言,都是一些很枯燥的东西,所以一定得有耐心,再者就是这里使用的不是Android Studio这个开发工具,用的是它的老娘Intellij IDEA,而且JetBrains推出Kotlin有一个原因也是希望能够促进IDEA的销售,大家学语法的时候因为并没有使用在app中,所以最好使用这个工具,操作起来更方便,直接从控制台看结果。这个工具的下载大家可以到官网上下载,也可以去谷歌找破解版软件,使用方式熟悉as的应该都知道怎么用的,下面介绍一下如何创建一个基于gradle的Kotlin的项目,我就不再写详细的教程了,这个没啥说的,直接放个录屏大家看一下吧:

下面来先写个HelloWorld吧:

package com.jarchie.cn

fun main(args:Array<String>) {
    println("Hello World!")
}

是不是发现跟Java的main函数有些类似,这样就可以在控制台输出一行Hello World!了:

二、Kotlin数据类型

2-1、Boolean类型

我们常说任何事物都具有两面性,那这个Boolean类型就是只有两面性,它只有两种状态true和false,它的写法很简单:

//Boolean类型的基本写法
val B1 : Boolean = true
val B2 : Boolean = false

val就是个关键字,用来定义常量,B1是常量名,然后冒号后面跟的是类型,等号后面是它的值,在Kotlin里面一行语句结束不用写分号。

2-2、Number类型

Number类型
分类 类型 位宽
浮点型 Double 64
Float 32
整型 Long 64
Int 32
Short 16
字节 Byte 8

1、Int

它是整型32位字长,定义Int类型数据跟定义Boolean类型的写法都是类似的,如下所示:

val aInt:Int = 8 //十进制 8
val bInt : Int = 0xff //十六进制 255
val cInt :Int = 0b00000011 //二进制 3

它是个有符号的数,最高位表示符号位,剩下的31位是数值范围,最大值就是2的31次方减1,最小值就是负的2的31次方,我们来写段代码打印一下这几个数据:

val maxInt: Int = Int.MAX_VALUE
val minInt: Int = Int.MIN_VALUE

fun main(args: Array<String>) {
    println("$aInt --- $bInt --- $cInt")
    println(maxInt)
    println(Math.pow(2.0, 31.0) - 1)
    println(minInt)
    println(-Math.pow(2.0, 31.0))
}

看一下结果,正是我们猜测的那样:

2、Long

它是长整型64位字长,就是比Int类型的范围大一些,没什么特别的东西,我们直接来上代码吧:

val aLong: Long = 123456789098
val bLong: Long = 123 //123通常被认为是Int类型,但是这里这样写就被认为是Long类型
val maxLong: Long = Long.MAX_VALUE
val minLong: Long = Long.MIN_VALUE

fun main(args: Array<String>) {
    println(123L) //单独打印123,那么它就是整型,如果想让它是长整型,需要在后面加个L
    println(maxLong)
    println(minLong)
    println(Math.pow(2.0, 63.0) - 1)
    println(-Math.pow(2.0, 63.0))
}

结果如下图所示:

这里需要注意理解的东西我在代码中写了注释,大家看一下理解了就行了。

3、Float

它是32位浮点型,也称单精度类型,声明Float类型数据的时候如果你直接这样写:val a:Float = 2.0,这时编译器会告诉你它是不可以的,在Kotlin中这样直接写2.0它其实是双精度类型的,那我们应该怎么写呢?需要在后面加个F,如下代码所示:

val aFloat: Float = 2.0F //需要加f
val bFloat: Float = 1E3f //10的三次方
val maxFloat: Float = Float.MAX_VALUE
val minFloat: Float = Float.MIN_VALUE

fun main(args: Array<String>) {
    println("$aFloat  $bFloat")
    println(maxFloat)
    println(minFloat)
}

结果如下:

这里有个问题不知道大家发现没有,这个最小值居然是个正数,惊不惊喜意不意外,点到源码里面查看一下:

看注释意思:保持Float的最小正非零值的常数,那我们该怎么改呢?应该取-Float.MAX_VALUE,负的最大值。

注意:Float类型因为是浮点型,它不像整型那样精确,所以在使用它的时候可能会有精度问题,如果涉及到金钱方面的程序,最好不要使用Float类型。

4、Double

它是双精度浮点型型64位字长,表示的范围比Float大很多,因为是指数级的增长,其余的都很类似,直接上代码吧:

val aDouble: Double = 3.0
val bDouble: Double = 3.1415926
val maxDouble: Double = Double.MAX_VALUE
//val minD:Double = Double.MIN_VALUE //最小的非零正数,并非最小值
val minDouble: Double = -Double.MAX_VALUE //将最大值取反,得到最小值

fun main(args: Array<String>) {
    println("$aDouble  $bDouble")
    println(maxDouble)
    println(minDouble)
}

结果如下:

5、Short

它是短整型16位字长,平时开发中可能很少用到,这里也提一下吧,很简单,就是取值范围比Int小一些,直接上代码:

val aShort: Short = 127
val maxShort: Short = Short.MAX_VALUE
val minShort: Short = Short.MIN_VALUE

fun main(args: Array<String>) {
    println(aShort)
    println(maxShort)
    println(minShort)
}

结果如下:

6、Byte

它是字节类型8位字长,一个字节就是8位,地球人都知道,由于字长较短,所以它能表示的数据范围也比较小,看代码:

//val byte:Byte = 235 //编译器会报错,这个整型的字面量大了,超出范围了,
// 所以如果要用整型的字面量给Byte类型赋值,因为它是有符号的,所以
//最大值肯定是2的7次方-1,那就是127,超出这个值肯定会报错
val maxByte: Byte = Byte.MAX_VALUE
val minByte: Byte = Byte.MIN_VALUE

fun main(args: Array<String>) {
    println(maxByte)
    println(minByte)
}

它的数据范围这里也在代码中作了解释,看下结果:

对于Long、Int、Short、Byte等几种类型单从上面的例子中可以看到它们都是整数,在表格中为什么我们单独将Byte作为一类呢?是因为实际开发中我们并不直接用Byte表示一个整数,而是把它作为数据流进行读写操作,作为一个二进制的数据操作。

2-3、Char类型

它是双字节16位的Unicode字符,它表示一个字符,对应Java中的Character,一个字符可以是一个字也可以是一个符号,字符用单引号''引起来,例如:'a'、'+','\n'等,来看代码:

val aChar: Char = '0'
val bChar: Char = '国'
//val cChar: Char = '\u000f' // \u表示的就是Unicode,后面的000f是一个Unicode编码
val cChar: Char = '\n' //换行符

fun main(args: Array<String>) {
    println(aChar)
    println(bChar)
    println(cChar)
}

结果如下:

像上面'\n'这样的字符其实就是转义字符,下面一起来看几个常用的转义字符:

Char类型
转义字符 含义
\t 制表符
\b 光标后退一个字符
\n 回车
\r 光标回到行首
\' 单引号
\" 双引号
\\ 反斜杠
\$ 美元符号,Kotlin支持美元符号开头的字符串模板

2-4、拆箱装箱

先来看一段Java代码:

public class HelloWorldJava {
    public static void main(String[] args) {
        int aInt = 5; //基本类型
        Integer aInteger = 5; //装箱类型
    }
}

在Java中int是基本类型,Interger是装箱类型,在Kotlin中其实并没有作这样的区分,所有的整数都叫int,kotlin中的int实际上是Java中的基本类型int和装箱类型Integer的合体,在需要的时候编译器会自动帮我们选择到底是用基本类型的int还是装箱类型的Integer,类似的像长整型、单精度浮点型、双精度浮点型 都有这样的情况出现,在kotlin中不再区分装箱和非装箱类型。

2-5、基础数据类型转换

在Java中,把一个int类型的值赋给long类型的变量是没有任何问题的,因为整型值表示的数据范围比长整型的要小,但是在kotlin中这种隐式转换是不被支持的,比如下面的这两行代码,必须要显示调用toLong()方法进行转换:

val a: Int = 5
//val b: Long = a //编译会报错
val b: Long = a.toLong()

2-6、字符串

1、字符串本质上就是'一串'Char,字符串可以直接使用双引号“”引起来定义,也可以使用字符进行构造,举个栗子:

val s1: String = "Hello Kotlin"
val s2: String = String(charArrayOf('H', 'e', 'l', 'l', 'o', ' ', 'K', 'o', 't', 'l', 'i', 'n')) //使用字符构造

fun main(args: Array<String>) {
    println(s1)
    println(s2)
}

结果很明显,都是输出一段Hello Kotlin:

2、在Kotlin中如何比较字符串呢?有些人可能会想到java中的等号了,在kotlin中使用“==”比较内容,等同于equals()方法,使用“===”比较对象,写段代码验证下:

fun main(args: Array<String>) {
    println(s1 == s2) //这个等同于equals方法
    println(s1 === s2) //这个比较的是引用地址
}

结果如下,跟前面说的是一致的:

3、字符串模板

在Kotlin中有个字符串模板,在字符串中使用“$”美元符号后面加上某个变量名可以引用某个变量值,例如:

val aNum: Int = 1
val bNum: Int = 1

fun main(args: Array<String>) {
    println("$aNum + $bNum = ${aNum + bNum}")
}

结果如下:

4、转义字符的使用

val money: Int = 100

fun main(args: Array<String>) {
    println("Hello \"Jarchie\"") //Hello "Jarchie"
    println("$money") //1000
    println("$$money") //$1000
    println("\$money") //$money
}

结果也很明显了:

5、原始字符串

使用"""aaa"""三个引号引起来,里面的内容会原封不动的输出,举个例子:

val rawString:String = """aaa
    \t      \n
aaa"""

fun main(args: Array<String>) {
    println(rawString)
}

输出结果如下,它不会做任何的转义:

三、类和对象

3-1、什么是类?

本篇内容只是简单的介绍一下类的概念和基本使用方法,这个本来是打算在写面向对象那部分再说的,后来想了想,还是得先说一下。类是一个抽象的概念,它是具有某些特征事物的概括,不特定指代任何一个具体的事物。举个栗子:我们平时日常生活中的人、车子、书本等,像上面介绍过的数、字符串也是类。

类的定义(写法):class <类名> (构造方法){<成员>}

3-2、什么是对象?

对象是一个具体的概念,与类是相对的,它是描述某一种类的具体的个体,举个栗子:某个人、某辆车(我的凯迪拉克,想象中。。。)、我的《安卓开发艺术探索》这本书。

3-3、类和对象的关系

  1. 一个类通常可以有很多个具体的对象
  2. 一个对象本质上只能从属于一个类
  3. 某一个具体的人,他是老师,但本质上还是属于人这一类,从这里可以看出类和对象是1...n也就是一对多的关系
  4. 对象也经常被称作“类的对象”或者“类的实例”

3-4、类的继承

  1. 提取多个类的共性得到一个更加抽象的类,我们称之为父类,
  2. 子类拥有父类的一切特征,
  3. 子类也可以自定义自己的特征,
  4. 所有的类都最终继承自Any

3-5、代码示例

说个程序里面经常开的玩笑:你有对象吗?没有我给你new一个,那我现在就给你new一个对象了哈,

//constructor()是构造方法,因为只有一个构造方法主构造方法,所以constructor可以省略不写,
// 如果类中没有定义其他成员的话{}也可以省略
class 对象 constructor(var 性格:String,var 长相:String,var 身材:String){
    init { //构造方法的方法体,在每次创建对象的时候都会调用这个方法体
        println("new了一个对象,她性格$性格,长相$长相,身材$身材")
    }
}

fun main(args:Array<String>) {
    val 你对象:对象 = 对象("温柔","甜美","苗条")
}

结果如下:

那现在我们来想一下哈,对象是new完了,那这个时候来了一个她的爱慕者,我们索性成为单身狗,这个单身狗同样的是有这么几个属性,那我们想要new一个单身狗,我们是不是还得再创建一个单身狗的类呢?其实有经验的大家应该能想到了,他们是有共性的,我们可以再做一层抽象,抽出来一个人的类,让这个单身狗和你的对象都去继承它,我们来看一下代码:

open class 人(var 性格: String, var 长相: String, var 身材: String){
    init {
        println("new了一个${this.javaClass.simpleName},Ta性格$性格,长相$长相,身材$身材")
    }
}

class 对象(性格: String, 长相: String, 身材: String) : 人(性格, 长相, 身材)
class 单身狗(性格: String, 长相: String, 身材: String) : 人(性格, 长相, 身材)

fun main(args: Array<String>) {
    val 你对象: 对象 = 对象("温柔", "甜美", "苗条")
    val singleDog: 单身狗 = 单身狗("孤僻", "奇丑", "矮小")
    println(你对象 is 人) //Kotlin中的is相当于Java中的instanceof
}

这样写是不是简单了一些,来看下结果:

代码中我们定义了一个“人”的类,然后让“对象”继承了“人”,中间使用了一个“:”冒号,这就是继承的写法了,这样对象就拥有了人的属性,对象就是人的子类,人就是对象的父类。在Kotlin中所有的类都直接或间接的是"Any"这个类的子类,不信的可以看下源码:这行注释的意思就是:Kotlin类层次结构的根。每个Kotlin类都有[Any]作为超类。

四、空类型和智能类型转换

4-1、空类型

在Java中,如果遇到对象为null的情况,会给我们返回一个空指针异常,导致我们的程序直接Crash。在Kotlin中任意类型都有可空和不可空两种,具体的情况见下方表格:

val notNull:String = null //错误,不能为空,编译器报错
val nullable:String? = null //正确,可以为空,在类型后面加"?"表示可空
notNull.length //正确,不为空的值可以直接使用
nullable.length //错误,可能为空,编译器报错,不能直接获取长度
nullable!!.length //正确,强制认定nullable不可空
nullable?.length //若nullable为空,则返回空

这里举个栗子,看的更明白点:

fun getName(): String? {
    return null
}

fun main(args: Array<String>) {
    val name1 = getName()
    println(name1?.length)

    val name:String? = "Hello Kotlin"
    println(name!!.length)

    val name2:String = getName()?:return
    println(name2.length)

    println("这句话不会被打印,因为它不会执行到这里")
}

结果如下:

4-2、类型转换

这里介绍三种:

第一种:Java Style类型转换

val sub:SubClass = parent as SubClass 类似于Java的类型转换,失败则抛异常

第二种:安全类型转换

val sub:SubClass? = parent as? SubClass 如果转换失败,返回null,不抛异常

第三种:智能类型转换

if(parent is SubClass) parent.<子类的成员> 编译器尽可能的推导类型,远离无用的类型转换

if(nullable != null) nullable.length 正确!因为我们确认nullable不为空!

还是同样的写段代码来加深印象吧:

package com.jarchie.kotlin.basic

open class Parent

class Child : Parent() {
    fun getName():String{
        return "Child"
    }
}

fun main(args: Array<String>) {
    val parent: Parent = Child() //强转
    if (parent is Child) {
        println(parent.getName()) //智能转换,无需强转
    }

    val string: String? = "Hello"
    if (string is String) //或者 if(string != null)
        println(string.length)

    val parents:Parent = Parent()
    val child :Child? = parents as? Child //不加?会抛出异常
    println(child)
}

来看下结果,看转换失败的也不会抛异常了,给我们返回个null:

五、包(Package)

  1. 它是命名空间
  2. 包的声明必须在非注释代码的第一行
  3. 类的全名:-com.jarchie.cn.basic.对象

这个跟Java中的包挺像的,不多说了,使用时导入对应的类的全名即可,比如下面这个:

六、区间(Range)

  1. 一个数学上的概念,表示范围
  2. ClosedRange的子类,IntRange最常用
  3. 基本写法:0..100表示[0,100]; 0 until 100表示[0,100); i in 0..100判断i是否在区间[0,100]中

举个栗子:

val range1: IntRange = 0..1024 //[0.1024]
val range2: IntRange = 0 until 1024 //[0,1024) 等同于[0,1023]
val emptyRange: IntRange = 0..-1

fun main(args: Array<String>) {
    println(emptyRange.isEmpty())
    println(range1.contains(1024))
    println(1024 in range1)
    println(range2.contains(1024))

//    for (number in range1) { //迭代数据
//        print("$number,")
//    }
}

结果如下:

七、数组

1、什么是数组

对应英文单字Array,跟数没啥关系,主要是组,它是一系列对象。

2、使用方式

基本写法:val array:Array<String> = arrayOf(...)

基本操作:

  • print array[i] 输出第i个成员
  • array[i] = "China" 给第i个成员赋值
  • array.length 数组的长度

3、基本类型的数组

为了避免不必要的装箱和拆箱,基本类型的数组是定制的

Java Kotlin
int[] IntArray
short[] ShortArray
long[] LongArray
float[] FloatArray
double[] DoubleArray
char[] CharArray

好了,知识点说完了,我们再来通过一段代码来加深下理解(注意代码中的“对象”类要复写toString()方法):

package com.jarchie.kotlin.array

import com.jarchie.kotlin.basic.对象

val arrayOfInt: IntArray = intArrayOf(1, 2, 3, 4)
val arrayOfChar: CharArray = charArrayOf('a', 'b', 'c')
val arrayOfString: Array<String> = arrayOf("我", "爱", "China")
val arrayOf对象: Array<对象> = arrayOf(对象("美女1号"), 对象("美女2号"), 对象("美女3号"))

fun main(args: Array<String>) {
    println(arrayOfInt.size) //获取数组长度

    for (char in arrayOfChar) { //遍历数组
        println(char)
    }

    println(arrayOf对象[1])
    arrayOf对象[1] = 对象("美女100号") //获取数组中某个下标的元素值并修改它的值
    println(arrayOf对象[1])

    println(arrayOfChar.joinToString("")) //将字符数组中的元素按照某种规则连接起来

    println(arrayOfInt.slice(1..2)) //字符串切片,这里我们获取整型数组的第1,2两位的元素
}

来看一下结果,看看你写对了吗:

写到这里已是深夜了,这一部分的内容就暂时写这么多,有不对的欢迎指正,下一篇是Kotlin的程序结构,敬请期待吧!

各位晚安!

发布了48 篇原创文章 · 获赞 47 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/JArchie520/article/details/103469421