Kotlin基础

参考文档

Kotlin是一种在Java虚拟机上运行的静态类型编程语言,被称为Android世界中的Swift,由JetBrains公司设计开发并开源。

Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。

Google I/O 2017年宣布Kotlin成为Android的官方开发语言。

为什么要选择Kotlin呢?

  • 简洁 大大减少样板代码的数量
  • 安全 避免空指针异常等整个类的错误
  • 互操作性 充分利用JVM、Android和浏览器现有的库
  • 工具友好 可以使用Java IDE或使用命令行构建

环境配置

  1. 下载JDK

https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

  1. 配置JDK

系统属性 > 高级 > 环境变量 > 系统变量

  • 新建变量,变量名 JAVA_HOME ,变量值 C:\jdk
  • 新建变量,变量名 CLASSPATH,变量值.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar
  • 选择Path变量,新建%JAVA_HOME%\bin%JAVA_HOME%\jre\bin

打开cmd命令行,输入命令验证。

$ java -version
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) Client VM (build 25.221-b11, mixed mode, sharing)

$ javac -version
javac 1.8.0_73
  1. 下载安装IntelliJ IDEA开发工具

https://www.jetbrains.com/idea/download/index.html

输入激活码:试用版教育激活码 仅供试用

YZVR7WDLV8-eyJsaWNlbnNlSWQiOiJZWlZSN1dETFY4IiwibGljZW5zZWVOYW1lIjoiamV0YnJhaW5zIGpzIiwiYXNzaWduZWVOYW1lIjoiIiwiYXNzaWduZWVFbWFpbCI6IiIsImxpY2Vuc2VSZXN0cmljdGlvbiI6IkZvciBlZHVjYXRpb25hbCB1c2Ugb25seSIsImNoZWNrQ29uY3VycmVudFVzZSI6ZmFsc2UsInByb2R1Y3RzIjpbeyJjb2RlIjoiSUkiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJBQyIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IkRQTiIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IlBTIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiR08iLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJETSIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IkNMIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiUlMwIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiUkMiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJSRCIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IlBDIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiUk0iLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJXUyIsInBhaWRVcFRvIjoiMjAxOS0xMS0yNiJ9LHsiY29kZSI6IkRCIiwicGFpZFVwVG8iOiIyMDE5LTExLTI2In0seyJjb2RlIjoiREMiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifSx7ImNvZGUiOiJSU1UiLCJwYWlkVXBUbyI6IjIwMTktMTEtMjYifV0sImhhc2giOiIxMTA1NzI3NC8wIiwiZ3JhY2VQZXJpb2REYXlzIjowLCJhdXRvUHJvbG9uZ2F0ZWQiOmZhbHNlLCJpc0F1dG9Qcm9sb25nYXRlZCI6ZmFsc2V9-rsJR5mlJcjibqRu1gQAMUCngMe8i+AOWIi+JZkNFYPET2G1ONcLPcIzoATTRi6ofkDm5l+3Y4HXjBPjVU6bHDdMBAzCnUqpXKsCknwSYyPSU0Y5pzuLvw6O9aPlQ46UBoTEC2BL5W6f11S7NlAq7tTbDuvFUynqSGAmTEfuZtKmzRmp20ejTPuMlSO7UqSkZvkg6YvSTrax1d2K+P9SAmVGZ9iC7AzBs4AwTf84QB9qHvE/Nh0oELSHWGG9hsZZ7sVghI/39/jPQFTp8GLFsl36ZPybPhGDam721zxS9H++/eJk23Jz3nxaRluE4dWmpHrDg1qBHp8qVpSFejg2QYw==-MIIElTCCAn2gAwIBAgIBCTANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBMB4XDTE4MTEwMTEyMjk0NloXDTIwMTEwMjEyMjk0NlowaDELMAkGA1UEBhMCQ1oxDjAMBgNVBAgMBU51c2xlMQ8wDQYDVQQHDAZQcmFndWUxGTAXBgNVBAoMEEpldEJyYWlucyBzLnIuby4xHTAbBgNVBAMMFHByb2QzeS1mcm9tLTIwMTgxMTAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxcQkq+zdxlR2mmRYBPzGbUNdMN6OaXiXzxIWtMEkrJMO/5oUfQJbLLuMSMK0QHFmaI37WShyxZcfRCidwXjot4zmNBKnlyHodDij/78TmVqFl8nOeD5+07B8VEaIu7c3E1N+e1doC6wht4I4+IEmtsPAdoaj5WCQVQbrI8KeT8M9VcBIWX7fD0fhexfg3ZRt0xqwMcXGNp3DdJHiO0rCdU+Itv7EmtnSVq9jBG1usMSFvMowR25mju2JcPFp1+I4ZI+FqgR8gyG8oiNDyNEoAbsR3lOpI7grUYSvkB/xVy/VoklPCK2h0f0GJxFjnye8NT1PAywoyl7RmiAVRE/EKwIDAQABo4GZMIGWMAkGA1UdEwQCMAAwHQYDVR0OBBYEFGEpG9oZGcfLMGNBkY7SgHiMGgTcMEgGA1UdIwRBMD+AFKOetkhnQhI2Qb1t4Lm0oFKLl/GzoRykGjAYMRYwFAYDVQQDDA1KZXRQcm9maWxlIENBggkA0myxg7KDeeEwEwYDVR0lBAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBCwUAA4ICAQAF8uc+YJOHHwOFcPzmbjcxNDuGoOUIP+2h1R75Lecswb7ru2LWWSUMtXVKQzChLNPn/72W0k+oI056tgiwuG7M49LXp4zQVlQnFmWU1wwGvVhq5R63Rpjx1zjGUhcXgayu7+9zMUW596Lbomsg8qVve6euqsrFicYkIIuUu4zYPndJwfe0YkS5nY72SHnNdbPhEnN8wcB2Kz+OIG0lih3yz5EqFhld03bGp222ZQCIghCTVL6QBNadGsiN/lWLl4JdR3lJkZzlpFdiHijoVRdWeSWqM4y0t23c92HXKrgppoSV18XMxrWVdoSM3nuMHwxGhFyde05OdDtLpCv+jlWf5REAHHA201pAU6bJSZINyHDUTB+Beo28rRXSwSh3OUIvYwKNVeoBY+KwOJ7WnuTCUq1meE6GkKc4D/cXmgpOyW/1SmBz3XjVIi/zprZ0zf3qH5mkphtg6ksjKgKjmx1cXfZAAX6wcDBNaCL+Ortep1Dh8xDUbqbBVNBL4jbiL3i3xsfNiyJgaZ5sX7i8tmStEpLbPwvHcByuf59qJhV/bZOl8KqJBETCDJcY6O2aqhTUy+9x93ThKs1GKrRPePrWPluud7ttlgtRveit/pcBrnQcXOl1rHq7ByB8CFAxNotRUYL9IF5n3wJOgkPojMy6jetQA5Ogc8Sm7RG6vg1yow==

入门程序

$ vim main.kt
fun main(args:Array<String>){
    println("hello world")
}
  • Kotlin文件是以.kt为后缀
  • fun表示函数声明,为固定写法。
  • main表示主方法,是程序的入口。
  • args:Array<String>表示接受的参数名称为args,数据类型为字符串。
  • println表示向控制台打印字符串,分号可以省略。

交互式编程

Kotlin命令行编译工具 https://github.com/JetBrains/kotlin

$ git clone https://github.com/JetBrains/kotlin.git

Kotlin和Java都是基于JVM的,使用Kotlin命令行编译工具前首先需要配置环境变量。

下载Kotlin命令行编译工具,解压到指定目录后,将bin目录添加到系统环境变量Path中。bin目录下包含编译和运行Kotlin所需的脚本。

$ kotlinc -version
info: Kotlin Compiler version 1.1.2-2

使用命令行工具Kotlin编译器编译Kotlin源文件为.jar包文件

$ kotlinc main.kt -include-runtime -d main.jar
  • -include-runtime表示让jar文件包含Kotlin运行库,从而可以直接运行。若需要生成的jar包是准备供其他Kotlin程序使用的,可以无需包含Kotlin的运行库,即可以省略此选项。需要注意的是,这样生成的jar文件不含Kotlin的运行库,所以需要确保使用时,运行时在classpath上的。
  • -d表示设置编译输出的名称,可以是.class.jar文件,也可以是目录。

使用Java命令运行.jar文件

$ java -jar main.jar
hello world

使用kotline命令运行Kotlin生成的.jar文件。

$ vim user.kt
class User(val name:String){
    fun output(){
        println("hello $name")
    }
}

fun main(args:Array<String>){
    User("junchow").output()
}
$ kotlinc user.kt -d user.jar
$ kotlin -classpath user.jar User
error: 'main' method not found in class User
main.jar  main.kt

变量

存储数据

计算机程序是跟数据打交道的,编写绝大多数程序都是在处理或记忆数据,Kotlin是如何声明和记忆数据的呢?

变量观点是培养程序设计能力的基础,在程序中的数据必须通过变量来处理,处理不同的数据必须使用不同的变量类型。

变量可以分为可变变量和不可变变量(常量)

可变变量

使用var关键字声明

var <变量标识符>:<数据类型> = <初始化值>

类型推导

不同的数据类型使用不同的的容器保存,Kotlin会通过类型推导自动推断出数据类型,当然最好是直接显式地指定数据类型。

例如:

fun main(args:Array<String>){
    //声明变量定义
    var id:Int = 100;
    println(id);
    id = 200;
    println(id);

    //系统自动推断变量类型
    var name = "junchow";
    println(name);
    name = "jc";//变量值可以修改但变量类型不可以修改
    println(name);

    //声明时若未初始化则必须提供变量类型
    var state:Byte;
    state = -128;//明确赋值
    println(state);
    state = 127;
    println(state);
}

不可变变量(常量)

使用val关键字声明不可变变量,不可变变量表示只能赋值一次的变量,类似Java中使用final修改的变量。可作为局部变量使用。

val <变量标识符>:<数据类型> = <初始化值>

例如:

fun main(args:Array<String>) {
    //声明不可变变量
    val pi:Float = 3.1415926f;
    println(pi);//3.1415925

    //声明不可变变量
    val e:Double;
    e = 2.17828184;
    println(e);//2.17828184
}

变量和常量都可以没有初始化值,但在引用前必须初始化。编译器支持自动类型判断,即声明时可以不指定数据类型,由编译器自动判断。

数据类型

Kotlin中一切皆对象,可以在任何变量上调用成员函数和属性。对于基本类型如数字、字符、布尔值可以在运行时表示为原生类型值。

Kotlin的基本数据类型包括

数字

Kotlin处理数字在某种成都上接近Java,但并不完全相同。

Kotlin中数字没有隐式类型转换,而Java却可以。比如Java支持将int类型隐式转换为long

整数

  • Byte 位宽度为8,表示整数,数据存储范围-128~127。
fun main(args:Array<String>){
    val byteMaxValue:Byte = Byte.MAX_VALUE
    val byteMinValue:Byte = Byte.MIN_VALUE
    println("byte max value is $byteMaxValue")//byte max value is 127
    println("byte min value is $byteMinValue")//byte min value is -128
}
  • Short 位宽度为16,表示整数,短整型,数据存储范围-32768~32767。
  • Int 位宽度为32,表示整数,标准整型,数据存储范围-2147483648~2147483647。
  • Long 位宽度为64,表示整数,长整型,数据存储范围-92233720368547775807~9223372036854775808,使用时需使用L进行标记。
fun main(args:Array<String>){
    val longMaxValue:Long = Long.MAX_VALUE
    val longMinValue:Long = Long.MIN_VALUE
    println("Long max value is $longMaxValue")//Long max value is 9223372036854775807
    println("Long min value is $longMinValue")//Long min value is -9223372036854775808
}

浮点数

  • Float 位宽度为32,表示小数,单精度浮点型,精确位数6位,使用时需使用fF表记。
  • Double 位宽度为64,表示小数,双精度浮点型,精确位数15~16位,浮点数的默认类型。

数值的字面常量

Kotlin不支持八进制,仅支持二进制、十进制、十六进制。默认为十进制。

  • 采用二进制方式赋值需要添加0b,其中b表示binary二进制意思。
fun main(args:Array<String>){
    //采用二进制方式赋值需要添加0b,其中b表示binary二进制意思。
    val binInt:Int = 0b0011
    println("binInt value is $binInt")//binInt value is 3
}

数字字面值中的下划线

Kotlin中可以添加下划线_,使数字常量更加易于阅读。

fun main(args:Array<String>) {
    val oneMillion = 1_000_000;
    println(oneMillion);//1000000
}

数字比较

Kotlin中没有基础类型,只有封装数字类型,每定义一个变量其实Kotlin会帮你封装了一个对象,这样就可以保证不会出现空指针。

当比较两个数字时,会比较数据大小和两个对象是或否相同。

Kotlin中使用两个==比较两个值的大小,使用三个===比较对象的地址。

fun main(args:Array<String>) {
    val pi:Double = 3.1415926;
    //使用对象比较符===,比较对象的地址是否相同,返回布尔值。
    println(pi === pi);//true
}

表示方式

在Java中数字是物理存储在JVM中的原生类型,当需要一个可空的引用(如Int?)或泛型时,系统会将数字装箱,需要注意的是数字装箱不一定会保留相同性。

fun main(args:Array<String>) {
    val i:Byte = 1;
    //println(i == 1);//Error:(3, 13) Kotlin: Operator '==' cannot be applied to 'Byte' and 'Int'
    println(i == i);//true
    println(i === i);//true

    val boxedVal:Byte? = i;
    val boxedValue:Byte? = i;
    println(boxedVal == boxedValue);//true
    println(boxedVal === boxedValue);//true
}

类型转换/显式转换

由于不同的表示方式,较小类型并非较大类型的子类。

fun main(args:Array<String>) {
    //声明一个装箱的Int,即java.lang.Integer
    val i:Int? = 1;
    //无法编译
    //隐式转换产生了一个装箱的Long,即java.lang.Long
    val l:Long? = i;//Error:(6, 19) Kotlin: Type mismatch: inferred type is Int? but Long? was expected
}

Kotlin中并不能隐式地将较小的类型转换为较大的类型。这也就意味着如果不进行显示转换的话,是不能够将Byte类型赋值给Int变量的。

fun main(args:Array<String>) {
    val b:Byte = 1;//字面值会自动进行静态类型检测
    //val i:Int = b;//Error:(3, 17) Kotlin: Type mismatch: inferred type is Byte but Int was expected
    //可是使用toInt方法对其进行显式地类型转换
    val i:Int = b.toInt();
    println(b);//1
    println(i);//1
}

显示类型转换方法

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

缺乏隐式类型转换很少会引起开发者的注意,因为类型会从上下文中推断出来,但是算术运算会重载而做适当转换。

在某些情况下也会进行自动类型转换,不过前提条件是可以根据上下文环境推断出正确的数据类型,而且数学操作符会做相应地重载。

fun main(args:Array<String>) {
    val l = 1L + 3;
    println(l);//4
}

字符

与Java不同的是Kotlin中的字符Char是不能够直接和数字进行操作的,字符类型的字面量必须使用单引号''包裹起来。简单来说,Kotlin中的字符不能直接当作数字。

需要注意的是,不同Java的是字符不属于数值类型,它是一种独立的数据类型,也就是说在Kotlin中字符不是数字。

fun main(args:Array<String>) {
    var c:Char = 'a';
    println(c == 96);//Error:(3, 13) Kotlin: Operator '==' cannot be applied to 'Char' and 'Int'
}

特殊字符可以使用反斜杠\进行转义,Kotlin中支持的转义字符包括:

  • \t
  • \b
  • \n
  • \r
  • \'
  • \"
  • \\
  • \$

当编码其它字符时需要使用Unicode转移序列

例如:将字符数字显式地转换为Int数字

fun decimalDigitValue(c:Char):Int{
    if(c !in '0'..'9'){
        throw IllegalArgumentException("Out of range");
    }
    return c.toInt() - '0'.toInt();
}

fun main(args:Array<String>) {
    val c:Char = '1';
    println(decimalDigitValue(c));//1
}

字符串

  • Kotlin中使用String 表示字符串
  • 字符串必须使用双引号引起来
  • 字符串无存储长度限制
  • 字符串是不可变的
  • 字符串的元素是字符,可使用索引运算符访问。
val name:String = "jc";
println(name[0]);//j
println(name.length);//2
//println(name[name.length]);//Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range
println(name[name.length - 1]);//c
  • 字符串的元素字符可使用for循环迭代

字符串拼接

  • 字符串可使用+操作符进行连接,+连接符也适用于连接字符串和其他类型的值,但是需要表达式中的第一个元素是字符串。
  • 多数情况下请优先使用字符串模板而非字符串连接符。
fun main(args:Array<String>) {
    val name:String = "jc";
    println(name[0]);//j
    println(name.length);//2
    //println(name[name.length]);//Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range
    println(name[name.length - 1]);//c

    for(c in name){
        println(c);//j c
    }

    val str:String? = "xxx";
    println(str);
    //println(str[0]);//Error:(6, 13) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

    var age:String = "18";
    age = age  + 1;
    println(age);//181

    var nick:String = "jc";
    println("nick = $nick");
    println("$.nick.length is ${nick.length}");


    var msg:String = "hello world\nwelcome";
    println(msg);
}

原始字符串

  • 原始字符串使用三个引号"""作为分界符进行包裹,其内部没有转义并且可以包含换行以及任何其他字符。

  • 去除原始字符串的前导空格trimMargin()trimMargin默认使用|作为边界前缀,可选择其他字符作为参数传入。

字符串模板

  • 字符串字面值可以包含模板表达式,也就是一些小段代码,会求值并将结果合并到字符串中。
  • 模板表达式以美元符号$开头,由简单的名字构成。
  • 模板表达式可使用花括号{}包裹任意表达式
  • 原始字符串与转义字符内部都支持模板
fun main(args:Array<String>){
    val i:Int = 100;
    println("i = $i");//i = 100

    val str:String = "junchow"
    println("str = $str $str.length = ${str.length}")//str = junchow junchow.length = 7

    val price:Double = 9.9
    val msg = """
        i = $i 
        str = $str 
        strlen = ${str.length}
        price = ${'$'}$price
        msg = ${str.replace("jun","jay")}
        """.trimIndent()
    println(msg)
}

字符串比较

  • 在Java中使用==比较两个字符串其实是比较字符串的内存地址
  • 在Kotlin中使用==比较两个字符串实际上比较的是字符串的内容,相当于Java中的equals方法。

双等号==

  • 如果作用于基本数据类型的变量则直接比较其存储的值是否相等。
  • 如果作用域引用类型的变量,则比较的是所指向的对象的内存地址。

三等号===

  • 如果作用于基本数据类型,只要数据类型不同结果就不等。如果是同类型比较则与==保持一致,即直接比较基本数据类型所存储的值是否相等。
  • 如果作用于引用类型,则于==保持一致,比较的是变量对指向的内存地址。
fun main(args:Array<String>){
    val i:Int = 10
    val l:Long = 10
    
    //==和===对于类型不同直接报错
    //println("i == l : ${i==l}")//Error:(6, 25) Kotlin: Operator '==' cannot be applied to 'Int' and 'Long'
    //println("i === l : ${i===l}")//Error:(8, 26) Kotlin: Operator '===' cannot be applied to 'Int' and 'Long'
    
    println("i == l.toInt() : ${i == l.toInt()}")//i == l.toInt() : true
    println("i.toLong() == l : ${i.toLong() == l}")//i.toLong() == l : true

    val str1 = "hello"
    val str2 = "hello"
    val str3 = "hell"
    //==作用于基本数据类型时比较的是存储值
    println("str1 == str2 : ${str1 == str2}")//str1 == str2 : true
    println("str1 == str3 : ${str1 == str3}")//str1 == str3 : false
    //===作用于基本数据类型是,同类型比较效果与==相同,对于字符串比较的都是值。
    println("str1 === str2 : ${str1 === str2}")//str1 === str2 : true
    println("str1 === str3 : ${str1 === str3}")//str1 === str3 : false
    //String类对equals方法进行了重写,比较的是字符串的值。
    println("str1.equals(str2) = ${str1.equals(str2)}")//str1.equals(str2) = true
    println("str1.equals(str3) = ${str1.equals(str3)}")//str1.equals(str3) = false
}

equals方法

  • equals方法不能作用于基本数据类型的变量,只能作用于引用类型的变量。
  • 如果没有对equals方法进行重写,则比较的是引用类型变量所指向的对象地址。
  • 比如在Kotlin中StringDate类对equals方法重写后比较的是对象所指向的内容。
data class User(var name:String, var age:Int){
    override fun equals(other:Any?):Boolean{
        if(other is User){
            return this.age == other.age && this.name == other.name
        }
        return false
    }
}

fun main(args:Array<String>){
    val u1:User = User("alice", 18)
    val u2:User = User("alice", 18)
    val u3:User  = User("lice", 18)
    val u4:User = u1

    println("u1 == u2 : ${u1 == u2}")//u1 == u2 : true
    println("u1 == u4 : ${u1 == u4}")//u1 == u4 : true

    println("u1.equals(u2) : ${u1.equals(u2)}")//u1.equals(u2) : true
    println("u1.equals(u3) : ${u1.equals(u3)}")//u1.equals(u3) : false
    println("u1.equals(u4) : ${u1.equals(u4)}")//u1.equals(u4) : true
}

字符串转换

如果要将Kotlin中的字符串转换为数字,前提条件是必须保证字符串是数字类型,不能出现数字和字符混合的字符串。转换时可使用toInt()方法进行字符串数字的转换。

如果要将数字转换成字符串,可以使用数字的toString()方法。

fun main(args:Array<String>) {
    var str = "10"
    var num = 100
    //将数字转换为字符串
    str = num.toString()
    println(str)//100
    //将字符串转换为数字
    num = str.toInt()
    println(num)//100
}

布尔

布尔类型使用Boolean表示,它只具有两个值truefalse

fun main(args:Array<String>){
    println(Math.pow(2.0, 100.0) > Math.pow(3.0, 75.0))//false
}
import kotlin.math.sqrt

fun main(args:Array<String>){
    // 根号运算比较
    print(sqrt(5.0)- sqrt(4.0) < sqrt(4.0) - sqrt(3.0))//true
}

若需要可空引用布尔则会被装箱

Kotlin中内置的布尔运算符

  • || 短路逻辑或
  • && 短路逻辑与
  • ! 逻辑非
fun main(args:Array<String>){
    var flag:Boolean = true
    var status:Boolean = false
    println("flag && status = ${flag && status}")//flag && status = false
    println("flag || status = ${flag || status}")//flag || status = true
    println("!flag = ${!flag}")//!flag = false
}

空值处理

Kotlin对比Java的一个最大的区别在于致力于消除【空引用】带来的危险。在Java中如果尝试访问一个【空引用】的成员可能会导致【空指针异常】NullPointerException(NPE)的出现。

在Kotlin中类型系统将可空类型和非空类型进行了区分。例如String字符串为不可空类型,String?则为可空类型,如果将不可空类型赋值为null则会导致编译无法通过。简单来说,可空类型主要是为了从编译层面上尽可能的减少NPE

可空操作符

Kotlin编译器默认变量不能为null,可使用可空操作符?表示变量为null

fun main(args:Array<String>){
    // var str1:String = null //Error:(2, 23) Kotlin: Null can not be a value of a non-null type String
    var str2:String? = null
    println(str2)// null
}

关闭空检查

fun main(args:Array<String>){
    var str:String? = null
    str.toInt()//Error:(3, 8) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
}

可使用!!关闭Kotlin的null空检查

fun main(args:Array<String>){
    val str1:String? = null
    //println(str1!!.toInt())//Exception in thread "main" kotlin.KotlinNullPointerException at TestKt.main(test.kt:3)

    val str2:String? = ""
    //println(str2!!.toInt())//Exception in thread "main" java.lang.NumberFormatException: For input string: ""

    val str3:String? = "0"
    println(str3!!.toInt())//0
}

空安全调用符

使用空安全调用符?表示空变量可以调用方法

fun main(args:Array<String>){
    val str1:String? = null
    println(str1?.toInt())//null

    val str2:String? = ""
    //println(str2?.toInt())//Exception in thread "main" java.lang.NumberFormatException: For input string: ""

    val str3:String? = "0"
    println(str3?.toInt())//0
}

Elvis操作符

fun main(args:Array<String>){
    val str1:String? = null
    println(str1?.toInt()?:-1)//-1

    val str2:String? = ""
    //println(str2?.toInt()?:-1)//Exception in thread "main" java.lang.NumberFormatException: For input string: ""

    val str3:String? = "0"
    println(str3?.toInt()?:-1)//0
}

Kotlin中有两种类型分别是非空引用类型、可空引用类型。对于可空引用如果希望调用它的成员变量或成员函数,直接调用会出现编译错误,有三种方式可以解决:

  • 调用前需要先检查因为可能为null
  • 使用varname?.length形式调用,如果varnamenull则直接返回varname.length
  • 使用varname!!.length形式调用,如果varnamenull则抛出空指针异常,否则返回varname.length

对于可空引用,调用其成员变量或函数必须先做检查否则编译器会报错。

fun main(args:Array<String>){
    val str:String? = ""
    if(str != null && str.isNotEmpty()){
        println("String of length is ${str.length}")
    }else{
        println("Empty string")//Empty string
    }
}
fun main(args:Array<String>){
    val str:String = ""
    //Condition 'str != null' always 'true'
    if(str != null && str.isNotEmpty()){
        println("String of length is ${str.length}")
    }else{
        println("Empty string")//Empty string
    }
}

使用非空引用必须保证它非空,使用可空类型每次必须做检查。为了简单使用,可空引用使用了一种安全的调用方式,即使用?.问号+点的方式进行调用。

fun main(args:Array<String>){
    // 如果`str`为空引用则直接返回`null`否则返回`str.length`

    val str1:String? = null
    println(str1?.length)//null

    var str2:String? = ""
    println(str2?.length)//0
}

可空引用的成员调用也才采用空安全调用符!!

fun main(args:Array<String>){
    val str1:String? = ""
    val result1 = str1!!.length
    println(result1)// 0

    val str2:String? = null
    val result2 = str2!!.length// Exception in thread "main" kotlin.KotlinNullPointerException at TestKt.main(test.kt:8)
    println(result2)
}

条件控制

Kotlin中的控制流主要分为ifwhenforwhile表达式。

If 表达式

  • Kotlin中一切都是表达式,也就是说一切都有返回值。
  • Kotlin中if是一个表达式,所以会返回一个值,因此无需使用三元运算符条件?满足:不满足
fun max(x:Int, y:Int):Int = if (x>y) x else y

fun main(args:Array<String>){
    println(max(1, 3))//3
}

If语句

  • 一个if语句包含一个布尔表达式和一条或多条语句,如果if条件没有异常,可以以if语句使用。

  • Kotlin中if的分支可以是代码块,以最后的表达式作为该代码块的值。如果if中的 一个分支返回了Unit那整个表达式也将返回为Unit

实例:使用in运算符来检测某个数字是否在指定区间中,区间格式为begin..end

fun main(args:Array<String>){
    val x = 2
    println(if(x in 0..9 ) true else false)//true
}

When 表达式

when即可被当作表达式和也可以被当作语句使用,当做表达式时符合条件的分支的值就是整个表达式的值,当作语句使用时则忽略别的分支的值。when将其参数和分支条件顺序比较,直到某个分支满足条件。

  • when类似其他语言中的switch操作符, whenelse类似switch语句中default,如果其他分支都不满足条件将会求值else分支。
fun main(args:Array<String>) {
    val x = 1
    when(x){
        1->println("x == 1")//x == 1
        2->println("x == 2")
        else -> {
            println("x is not 1 and 2")
        }
    }
}
  • 如果很多分支需要使用相同的方式进行处理,则可以将多个分支条件放在一起,使用逗号分隔。
fun main(args:Array<String>) {
    val x = 1
    when(x){
        0,1->println("x == 0 or x == 1")//x == 0 or x == 1
        2->println("x == 2")
        else -> {
            println("x is not 1 and 2")
        }
    }
}
  • when可以检测一个值在in或不在!in一个区间或者集合中
fun main(args:Array<String>) {
    val validNumbers = arrayOf(10, 20, 30)
    val x = 10
    when(x){
        in 0..9 -> println("x is in the range 0 to 9")
        !in 0..99 -> println("x is outside the range 0 to 99")
        in validNumbers -> println("x is valid")//x is valid
        else -> {
            println("none of the above")
        }
    }
}
fun main(args:Array<String>) {
    val item = "boss"
    val items = setOf("boss", "manager", "worker")
    when{
        item in items -> println("success")
        else -> println("error")
    }
}
  • when可以检测一个值是is或不是!is一个特定类型的值,需要注意的是,由于智能转换可以访问该类型的方法和属性而无需任何额外的检测。
fun hasPrefix(x:Any, prefix:String) = when(x){
    is String->x.startsWith(prefix)
    else -> false
}
fun main(args:Array<String>) {
    println(hasPrefix("web_admin", "web_"))//true
}
  • when可以用来取代if-else if链,如果不提供参数则所有分支条件都是简单的布尔表达式,当一个分支的条件为真时则执行该分支。
fun isOdd(x:Int):Boolean = if(x%2 == 1) true else false
fun isEven(x:Int):Boolean = if(x%2 == 0) true else false
fun main(args:Array<String>) {
    val x = 3
    when{
        isOdd(x)->print("x is odd")
        isEven(x)->print("x is event")
        else->println("x is funny")
    }
}

循环控制

Loop和Range

Kotlin循环中所使用的区间定义和Java中有很大的不同

fun main(args:Array<String>) {
    val items = 1..100// [1,100]
    var result = 0
    for(item in items){
        result += item
    }
    println(result)//5050
}

区间定义

开区间使用(begin, end)表示,开区间不包含两端的点。闭区间使用[begin, end]表示,闭区间包含两端的点。

  • 使用1..100表示[1,100]的闭区间,包含1和100。
  • 使用1 until 100表示前开后闭区间[1, 100) ,不包含100。
fun main(args:Array<String>) {
    val items = 1 until 100
    var result = 0
    for(item in items){
        result += item
    }
    println(result)//4950
}

跨步遍历

Kotlin中使用step指定每次遍历是以多少个为单位

fun main(args:Array<String>) {
    val items = 1 until 100
    var result = 0
    for(item in items step 2){
        result += item
    }
    println(result)//2500
}

反转集合

fun main(args:Array<String>) {
    val items = 1 until 100
    for(item in items.reversed() step 2){
        println(item)
    }
}

集合个数

fun main(args:Array<String>) {
    val items = 1 until 100
    println(items.count())//99
}

For 循环

Kotlin中for循环可以对任何提供迭代器iterator的对象进行遍历,相当于C#中的foreach循环。

Kotlin中的for循环可以便利任何提供了迭代器的对象

  • 有一个成员函数或扩展函数iterator()的返回类型
  • 有一个成员函数或扩展函数next()
  • 有一个成员函数或扩展函数hasNext()并返回布尔值Boolean

iteratornexthasNext三个函数需要标记为operator

for(item in collection) print(item)

for循环体可以是一个代码块

for(item:Int in items){

}

正序遍历

fun main(args:Array<String>) {
    for(item in 0 until 9 step 2){
        println(item)//0 2 4 6 8
    }
}

倒序遍历

Kotlin的for循环如果需要在数字区间上迭代,可使用区间迭代器。

fun main(args:Array<String>) {
    for(item in 9 downTo 0 step 2){
        println(item)//9 7 5 3 1
    }
}

通过索引遍历数组和列表

fun main(args:Array<String>) {
    val array = arrayOf(2,4,6,8, 10)
    for(index in array.indices){
        println("index = $index value = ${array[index]}")
    }
}

在区间上遍历会被编译成优化的实现而非创建额外对象,看使用库函数withIndex

fun main(args:Array<String>) {
    val array = arrayOf(2,4,6,8, 10)
    for((index, value) in array.withIndex()){
        println("index = $index value = $value")
    }
}
fun main(args:Array<String>) {
    val list = listOf<String>("alice", "ben", "carl")
    for(item in list){
        println(item)
    }
    //Error:(6, 27) Kotlin: Destructuring declaration initializer of type String must have a 'component1()' function
    for((index, value) in list.withIndex()){
        println("index = $index value = $value")
    }
}

While 循环

  • while循环是最基本的循环,表达式结构
while(布尔表达式){
  //循环内容
}
  • do..while循环对于while语句而言,如果不满足条件则不能进入循环。当需求即使不能满足,也至少执行一次。
do {
  //代码语句
}while(布尔表达式)

例如:

fun main(args:Array<String>) {
    var x = 5;
    while(x > 0){
        println(x--)
    }
    
    println("------------------------------")
    
    var y = 5
    do{
        println(y--)
    }while(y>0)
}

Repeat 循环

fun main(args:Array<String>) {
    repeat(5){
        it->println("$it")
    }
}

Kotlin相对于Java新加入的特性,取代for(int i=0; i<5; i++)这种简单的重复工作。

fun main(args:Array<String>) {
    repeat(5){
        println("$it")
    }
}

函数

  • 计算机程序中的函数是程序执行的小片段,这些小片段可以有机的组合在一起,完成复杂的业务功能。
fun 函数名(参数名: 参数类型): 返回值类型{
  函数体
}

主函数main函数是Kotlin程序的入口函数,是Kotlin程序运行认寻找的第一个函数。

fun main(args:Array<String>):Unit {
  // 函数体
}
  • fun声明函数的关键字
  • main函数名
  • args参数名
  • Array<String>参数类型,表示字符串数组。
  • Unit 返回值类型,表示没有返回值,可省略。

函数声明

  • Kotlin中函数使用关键字fun声明,函数的参数格式为参数:数据类型
fun echo(message:String):Unit{
    println(message)
}

fun double(x: Int): Int{
    return x * 2;
}

fun sum(x:Int = 10, y:Int = 10):Int{
    return x + y;
}

fun main(args:Array<String>){
    echo(double(10).toString())//20
    echo(sum().toString())//20
}

函数参数

函数的参数使用Pascal表示法定义,即name:type,其中参数使用逗号分隔,,每个参数必须有显示类型。

import kotlin.math.pow

fun powOf(number:Int, exponent:Int): Double {
    return number.toDouble().pow(exponent.toDouble())
}

fun main(args:Array<String>){
    println(powOf(2, 3))//8.0
}

Kotlin中函数的参数可以分为两种分别是默认参数和具名参数。

默认参数

Kotlin提供了在函数定义中制定默认参数的工具,如果调用函数但不传递任何参数,则会使用默认参数作为函数定义的参数。当使用参数调用函数时,传递参数将用作函数定义中的参数。

fun run(id:Int = 0, name:String = ""){
    println("parameter in function definition $id and $name")
}
fun main(args:Array<String>) {
    //函数调用时不传递参数
    run()//parameter in function definition 0 and 
    //函数调用时传递部分参数
    run(1)//parameter in function definition 1 and 
    //函数调用时传递所有参数
    run(2, "jc")//parameter in function definition 2 and jc
}
  • 函数参数可以拥有默认值,当省略相应的参数时会使用默认值,这样做的好处可以减少重载次数。

  • Kotlin中函数默认值通过类型后面的=以及给出的值来定义

  • 覆盖方法会使用与基类型方法相同的默认参数值,当覆盖一个带有默认参数值的方法时,必须从签名中深谷额默认参数值。

class User(val username:String="guest", val nickname:String) {
    fun out(){
        println("username = $username nickname = $nickname")
    }
    fun output(username:String="nobody", nickname:String="nothing"){
        println("username = $username nickname = $nickname")
    }
}
fun main(args:Array<String>){
    User(nickname="jc").out()//username = guest nickname = jc
    User(nickname="jc").output()//username = nobody nickname = nothing
    User(nickname="jc").output("junchow", "jun")//username = junchow nickname = jun
    
}

具名参数

Kotlin函数中的具名参数又称为命名参数,具名参数是一个参数,在函数调用中定义参数的名称,定义函数调用的参数名称,并检查匹配函数定义中的名称然后分配给它。

fun run(id:Int = 0, name:String = ""){
    println("parameter in function definition $id and $name")
}
fun main(args:Array<String>) {
    run(name="junchow")//parameter in function definition 0 and junchow
}

具名参数可以在调用函数时使用命名的函数参数,当一个函数中存在大量参数或默认值时使用具名参数将会非常方便。另外,使用具名参数将使代码更加具有可读性。

位置参数

当一个函数调用混用位置参数和具名参数时,所有位置参数都要放在第一个命名参数之前。

可变参数

可以使用*星号操作符将可变数量参数vararg以命名形式传入函数。

函数返回值

  • 单表达式函数是指可以使用表达式作为函数体,返回类型将会进行自动推断。
fun sum(x:Int, y:Int)  = x + y
fun main(args:Array<String>){
    println(sum(100, 200))//300
}
  • 若函数无返回值,函数返回值类型可使用Unit类型,也可以省略,因此是可选的。

Kotlin中的Unit相当于Java中的voidUnit是一种只有一个值Unit的类型,这个值表示函数无需显式返回。

  • 函数的变长参数使用vararg关键字进行标识
fun vars(vararg arg:Int){
    for(item in arg){
        println(item)
    }
}
fun main(args:Array<String>){
    vars(1,2,3,4,5)
}

函数表达式

Kotlin函数表达式实际上指的是函数式的表达式,函数表达式可分为两种分别是单表达式函数和Lambda表达式。

单表达式

  • 当函数返回单个表达式时可以省略花括号并且在=等号之后的指定函数体。
fun main(args:Array<String>) {
   println(double(2))//4
}

fun double(x:Int):Int = x * 2
  • 当函数返回值类型可以由编译器推断时,可以省略返回类型,即显式声明返回类型是可选的。
fun main(args:Array<String>) {
   println(double(2))//4
}

fun double(x:Int) = x * 2

Lambda 表达式

Lambda表示一个没有名字的函数,Lambda使用花括号{}定义,将变量作为参数和函数体,函数体在变量之后写入,后跟->运算符。

{variable -> body_of_function}

例如:求两个数字相加之和

普通函数式写法

fun sum(x:Int, y:Int):Int{
    return x+y
}
fun main(args:Array<String>) {
    println(sum(1, 2))//3
}

Lambda写法

fun main(args:Array<String>) {
    val sum = { x: Int, y: Int -> x + y }
    println(sum(1, 2))//3
}

Lambda可以声明一个函数后赋值给一个变量

fun main(args:Array<String>) {
    val sum:(Int, Int)->Int = {x,y -> x+y}
    println(sum(1, 2))//3
}

Lambda表达式的完整语法即函数类型的字面量的形式是

fun main(args:Array<String>) {
    val double = {x:Int -> x * 2}
    println(double(2))//4

    val sum = {x:Int, y:Int -> x + y}
    println(sum(1, 2))//3

    val total:(Int, Int) -> Int = {x,y -> x+y}
    println(total(10, 20))
}
  • Lambda表达式总是被大括号{}包裹
  • Lambda表达式完整语法形式的参数声明应当放在括号内,并有可选的类型标注。
  • Lambda表达式的函数体跟在一个->符号后,若推断出Lambda的返回类型不是Unit则主体的最后一个表达式会视为是返回值。

集合

Kotlin中的集合分为两种分别是可变集合类Mutable和不可变集合类Immutable,集合类存放的都是对象的引用而非对象本身。集合类型主要包括列表List、集合Set、映射Map三种。

列表、集合、映射三者之间的共性是什么呢?

  • 都是接口并非实际的类
  • 都继承自Collection<out E>接口,Collection<out E>又继承自Iterable<out T>接口,Collection几乎只实现了isEmpty()get()contains()等方法和size属性,这一点和Java类似。
  • 三类集合类型分别存在MutableList<E>MutableSet<E>MutableMap<K, V>接口,接口中提供了修改和操作集合的方法,比如add()clear()remove()等函数。

在定义集合类型变量的时候,如果使用了List<E>Set<E>Map<K, V>声明时该集合则是不可变集合。使用MutableList<E>MutableSet<E>MutableMap<K,V>的时候集合才会转变成可变类型的集合。

列表

Kotlin的列表是一个接口合同通用的元素集合,列表接口继承自Collection <T>类的形式,是不可变的,其方法只支持读取数据功能。若想要使用列表接口,首先需要使用listOf()listOf<T>()函数,List元素上哪找顺序插入,包含于数据相同的索引值。

接口声明

public interface List<out E>:Collection<E> (source)

一个接口是不能够直接被实例化的,Kotlin提供了相应的标准库对列表进行初始化。

  • 声明并初始化不可变列表的集合:使用listOf()函数
fun main(args:Array<String>) {
    val list = listOf("alice", "bob", "carl")//read only, fix-size
    for(element in list){
        println(element)
    }
}
  • 声明并初始化可变列表的集合:使用mutableListOf()函数

不可变列表

创建

Kotlin中使用listOf函数创建一个不可变(只读)的列表,listOf构建函数包含三个重载函数。

  • listOf()用于创建没有元素的空列表
@kotlin.internal.InlineOnly
public inline fun <T> listOf(): List<T> = emptyList()
fun main(args:Array<String>) {
    val list:List<Int> = listOf()
    println(list)//[]
}
  • listOf(element:T) 用于创建只有一个元素的列表
@JvmVersion
public fun <T> listOf(element:T):List<T> = java.util.Collections.singletonList(element)
fun main(args:Array<String>) {
    val list:List<Int> = listOf(0)
    println(list)//[0]
}
  • listOf(vararg elements:T)用于创建拥有多个元素的列表
public fun <T> listOf(varargs elements:T):List<T> = if(elements.size >= 0) elements.asList() else emptyList()
fun main(args:Array<String>) {
    val list:List<Int> = listOf(0, 2, 4, 6, 8)
    println(list)//[0, 2, 4, 6, 8]
}
查找

按索引获取元素

fun main(args:Array<String>) {
    val list = listOf(1, 2, 3, 4)
    println(list)//[1, 2, 3, 4]
    //获取列表元素个数
    println("list.size = ${list.size}")
    println("list.count() = ${list.count()}")
    //根据索引获取列表元素
    println("list[0] = ${list[0]}")
    println("list[list.size - 1] = ${list[list.size - 1]}")
    println("list[list.count() - 1] = ${list[list.count() - 1]}")
    //使用get函数指定索引获取列表元素
    println("list.get(0) = ${list.get(0)}")
    println("list.get(list.size - 1) = ${list.get(list.size - 1)}")
    println("list.get(list.count() - 1) = ${list.get(list.count() - 1)}")
    println("list.getOrNull(list.count()) = ${list.getOrNull(list.count())}")
    println("list.getOrElse(list.count(), {it}) = ${list.getOrElse(list.count(), {it})}")

    println("list.elementAt(1) = ${list.elementAt(1)}")
    println("list.elementAt(list.size - 1) = ${list.elementAt(list.size - 1)}")

    println("list.first() = ${list.first()}")
    println("list.last() = ${list.last()}")
}

获取列表中的部分产生子列表

fun main(args:Array<String>) {
    val list = (0..9).toList()
    println(list)//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    val sublist = list.subList(0, 3)
    println(sublist)//[0, 1, 2]
}

查找元素位置

线性查找

fun main(args:Array<String>) {
    val seq = 1..9
    println(seq)//1..9

    val list = seq.toList()
    println(list)//[1, 2, 3, 4, 5, 6, 7, 8, 9]

    var item = list.indexOf(2)
    println(item)//1

    item = list.indexOfFirst { it%2 == 0 }
    println(item)//1

    item = list.indexOfLast{it%2 == 1}
    println(list)//[1, 2, 3, 4, 5, 6, 7, 8, 9]

    item = list.lastIndexOf(2)
    println(item)//1
}

在有序列表中二分查找

Comparator二分查找

比较函数二分查找

可变列表

可变列表MutableList中,除了继承列表List中的函数外,另外新增了addaddAllremoveremoveAllremoveAtsetclearretainAll等更新修改的操作函数。

创建

fun main(args:Array<String>) {
    val mlist = mutableListOf<Int>(1,3,5,7,9)
    println(mlist)//[1, 3, 5, 7, 9]
    
    mlist.add(0, 0)
    println(mlist)//[0, 1, 3, 5, 7, 9]
}

使用toMutableList函数可以将不可变列表转化为可变列表

fun main(args:Array<String>) {
    val list:List<Int> = listOf<Int>(2,4,6,8)
    println(list)//[2, 4, 6, 8]

    //将可不变列表转换为可变列表
    val mlist = list.toMutableList()
    mlist.add(0, 0)
    println(mlist)//[0, 2, 4, 6, 8]
}

写入

创建

更新

删除

排序

映射

Map类似一个词典,词典是有索引的,词典会根据索引查询对应的具体内容。

Kotlin中Map映射集合是一个接口和通用的元素集合,Map接口以键值对的形式保存数据,映射的键是唯一的,每个键只保留一个值。键和值可以是不同类型的一对儿,例如<Int, Int><Int, String><Char, String>等。Map映射接口是不可变的,大小固定,方法只支持只读访问。若要使用Map接口需要使用mapOf()mapOf<k, v>()函数。

接口声明

Interface Map<K, out V>(source)

接口属性

  • abstract val entries:Set<Entry<K, V>>返回当前Map中Set接口的所有键值对
  • abstract val keys:Set<K>返回当前Map接口中所有的键
  • abstract val values:Collection<V>返回当前Map接口中所有值得读取集合,集合中可能包含重复值。

分类

Kotlin中Map可以分为两种分别是只读Map和可变Map,其中可变Map又可以分为三种分别是MutableMap、HashMap、LinkedMap。

Map<K,V>类型集合和List和Set是有差别的,Map分为可变Map类型和不可变Map类型:

  • 可变Map类型集合 初始化使用mutableMapOf()函数
fun main(args:Array<String>) {
    val mmap = mutableMapOf("k1" to 1, "k2" to 2)
    println(mmap)//{k1=1, k2=2}
}
  • 不可变(只读)Map类型集合 初始化使用mapOf()函数
fun main(args:Array<String>) {
    val map = mapOf("k1" to 1, "k2" to 2)
    println(map)//{k1=1, k2=2}
}

当Map的键存在重复时,集合会过滤掉之前重复的元素。

创建

  • mapOf() 创建不可变的映射集合,接受0个或多个键值对作为Map的元素。
  • mutableMapOf() 创建可变的MutableMap集合,接受0个或多个键值对作为Map的元素。
  • hashMapOf()创建可变的HashMap集合,接受0个或多个键值对作为Map的元素。
  • linkedMapOf()创建可变的LinkedHashMap集合,接受0个或多个键值对作为Map的元素。
  • sortedMapOf()创建可变的TreeMap集合,接受0个或多个键值对作为Map的元素。

标准IO

人机交互

  • 意念交互Brain Interface
  • 语音交互 Voice Interface
  • 眼动追踪Eye Interface
  • 体感交互`Body Interface

Kotlin中执行标准输入操作将会把字节流从输入设备(键盘)传递到主),执行标准输出操作将会从主存中把字节流传递到输出设备(屏幕)。

标准输出

Kotlin使用标准输出方法print()println()函数执行输出操作,print()println()函数将会分别在内部调用System.out.print()System.out.println()方法。

print()prntln()方法之间的区别在于print()方法用于打印提供参数的值,而println()方法用于打印提供参数的值后并将光标移动到下一行开头。

标准输入

Kotlin中使用标准库函数readLine()将从标准输入流中读取字符串输入行并返回读取的行或null

fun main(args:Array<String>) {
    print("name:")
    val name:String? = readLine()
    print("age:")
    val age:Int = Integer.valueOf(readLine())

    println("name:$name age:$age")
}

Kotlin中使用readLine函数时返回一个可为null的字符串,默认情况下是不能直接将可空字符串转换成为数字的,因此这里采用!!关闭空检查,从而能够计算出结果。

fun main(args:Array<String>) {
    println("请输入:")
    val input1:String? = readLine()
    println("请输入:")
    val input2:String? = readLine()

    val number1:Int = input1!!.toInt()
    val number2:Int = input2!!.toInt()

    println("$number1 + $number2 = ${number1+number2}")
}

使用readLine()函数时String字符串之外的输入行将显式转换为对应的类型,若要输入其他数据类型而非字符串,则需要使用Java标准库中的java.util.Scanner类的Scanner对象。

import java.util.*

fun main(args:Array<String>) {
    val input = Scanner(System.`in`)
    
    print("age:")
    val age = input.nextInt()
    
    println("age = $age")
}

Java标准库中Scanner类的nextInt()是用采用整数输入并存储在整形变量中的方法,其他数据类型支持

  • Boolean布尔:nextBoolean()
  • Float浮点数:nextFloat()
  • Double双精度:nextDouble()
  • Long长整形:nextLong()

异常处理

异常是程序中发生的运行时问题,会导致程序终止。异常可能是由于内存空间不足、数组越界、条件除零等引发的,异常处理就是要在程序执行期间处理这种类型的问题。

fun main(args:Array<String>) {
    val percent = 101
    if(percent !in 0..100){
        //Exception in thread "main" java.lang.IllegalArgumentException: throw an exception at TestKt.main(test.kt:5)
        throw IllegalArgumentException("throw an exception")
    }
}

异常处理有助于提高程序的容错性和可读性,从功能的实现上和错误代码分离,因此即使出现了异常,程序也不会崩溃掉。

Kotlin中的异常处理与Java或其他语言极为相似,当一个函数以正常方式结束或当错误发生时会抛出异常,函数调用者捕获并处理它,如果没有则会重新在调用栈中向上抛。

捕获异常

Kotlin使用try...catch...finally子句处理异常,Kotlin并不会去区分检查异常和非检查异常。当无需指定一个函数可能会抛出的异常,甚至可能会无需处理任何异常。

import java.io.BufferedReader
import java.io.StringReader

fun read(reader: BufferedReader):Int?{
    try{
        val line = reader.readLine()
        return Integer.parseInt(line)
    }catch(ex:Exception){
        return null
    }finally{
        reader.close()
    }
}

fun main(args:Array<String>) {
    val buf = BufferedReader(StringReader("1234"))
    println(read(buf))//1234
}

Kotlin中如果在函数内部抛出了异常,或在函数内部调用其它函数时抛出异常。此时函数将在抛出异常的地方结束。比如不希函数结束,那此时就必须捕获并处理这个异常。

异常处理中使用了四种不同的关键字,它们分别是trycatchfinallythrow

  • try 表示代码块中可能会出现异常代码
  • catch 表示对try中可能出现的异常做处理,可以存放在多个cache代码块,
  • finally 表示用于回收try中打开的资源,异常处理机制保证finanlly中的代码总被助兴。即使try...catch中执行了return,也仍然会执行,但需要执行`System.exit(!)退出虚拟机就不能执行。

所需注意的是

抛出异常

Kotlin中所有异常类都是Throwable类的子类,抛出异常对象则使用throw表达式。对于每个异常来说,不仅仅包括异常的信息,还可以选择性包括异常的原因

# 使用throw表达式抛出异常
throw MyException("throw an exception")

Kotlin中创建异常实例时无需使用new关键字
Kotlin中的throw结构是一个表达式,能作为另一个表达式的一部分使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容