目录
前言
今天来学习一下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类型
分类 | 类型 | 位宽 |
---|---|---|
浮点型 | 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'这样的字符其实就是转义字符,下面一起来看几个常用的转义字符:
转义字符 | 含义 |
---|---|
\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...n也就是一对多的关系
- 对象也经常被称作“类的对象”或者“类的实例”
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)
- 它是命名空间
- 包的声明必须在非注释代码的第一行
- 类的全名:-com.jarchie.cn.basic.对象
这个跟Java中的包挺像的,不多说了,使用时导入对应的类的全名即可,比如下面这个:
六、区间(Range)
- 一个数学上的概念,表示范围
- ClosedRange的子类,IntRange最常用
- 基本写法: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的程序结构,敬请期待吧!
各位晚安!