文章目录
概述:
- 编程语言分为两种:编译型语言和解释型语言
编译型语言:编译器将编写的源代码一次性编译成计算机可识别的二进制文件,然后计算机直接执行。
解释型语言:有一个解释器,程序运行时解释器一行行读取编写的源代码,实时将这些源代码解释成计算机可识别的二进制数据后再执行 - java代码编译之后生成一种特殊的class文件,只有Java虚拟机才能识别,这个Java虚拟机担当的是解释器的角色,Java属于解释型语言。
- Kotlin工作原理:
开发了一门新的编程语言,然后自己做了编译器,让它将这门新语言的代码编译成同样规格的class文件,只要是符合规格的class文件,java虚拟机都能识别 - Kotlin优点
语法简单,没有分号,语法高级,增加了很多现代高级语言的语法特性,开发效率大大提升,更安全,几乎杜绝了空指针异常,和Java百分百兼容
1 变量
两种关键字
关键字 | 是否可变 |
---|---|
val(value) | 声明一个不可变的变量 ,初始赋值后再也不能重新赋值,对应java中的final变量 |
var (variable) | 声明一个可变的变量,初始赋值后还能重新赋值,对应java中的非final变量 |
注意:永远优先使用val声明变量,当val无法满足需求时再使用var
1.2 数据类型
Kotlin使用了对象数据类型,Java中int是关键字,Kotlin中Int是一个类,拥有自己的方法和继承结构
2 函数
2.1 概念
函数和方法是同一个概念,是用来运行代码的载体
fun methonName(param1: Int, param2 :Int):Int{
return 0;
}
fun:定义函数的关键字
methonName:函数名
(param1: Int, param2 :Int):声明函数接收什么参数
Int:返回的数据类型
2.2 语法糖
当一个函数中只有一行代码时,可以不必编写函数体,直接将唯一的一行代码写在函数尾部,中间用等号连接
fun largeNumber(num1:Int,num2:Int):Int= max(num1,num2)
Kotlin的类型推导机制:
fun largeNumber(num1:Int,num2:Int) = max(num1,num2)
3 程序的逻辑控制
3.1 if条件语句
fun largerNumber(num1:Int,num2:Int):Int {
var value=0;
if(num1 > num2){
value = num1;
}else{
value = num2;
}
return value;
}
if语句是可以有返回值的,返回值是if语句中每一个条件中最后一个代码的返回值
fun largerNumber(num1:Int,num2:Int):Int{
val value = if(num1 > num2){
num1
}else{
num2
}
return value;
}
还可以简化成:
fun largerNumber(num1:Int,num2:Int):Int{
return if(num1>num2){
num1
}else{
num2
}
}
写成语法糖:
fun largerNumber(num1:Int,num2:Int) = if(num1>num2){
num1
}else{
num2
}
3.2 when语句
用一个例子解释它的用法:
用if语句写:
fun getScore(name:String) = if(name == "Tom"){
86
}else if (name == "Jim"){
77
}else if (name == "Jack"){
95
}else if (name == "Lily"){
100
}else{
0
}
使用when语句:
fun getScore(name:String) = when(name){
"Tom"->86
"Jim"->77
"Jack"->95
"Lily"->100
else ->0
}
- when语句有返回值
- 匹配值 -> { 执行逻辑 }
- when语句还允许进行类型匹配
例如:
fun checkNumber(num:Number){
when (num){
is Int -> println("number is Int");
is Double ->println("number is Double");
else ->println("number not support");
}
}
1.is关键字是类型匹配的核心 = java中的Instanceof关键字
2.Number类型的参数,Kotlin内置的一个抽象类,Int,Float,Double等与数字相关的类都是它的子类
3.3 循环语句
3.3.1 while循环没有区别
3.3.2 for-in 循环
…关键字——两端闭区间
val range = 0…10,相当于[0,10],…是创建两端闭区间的关键字
fun main(){
for(i in 0..10){
println(i)
}
}
until关键字——左闭右开的区间
val range = 0 until 10,相当于[0,10)
step关键字——跳过一些元素
fun main(){
for(i in 0 until 10 step 2){
println(i)
}
}
downTo关键字——降序的区间
i in 10 downTo 1 = [10,1]
4.面向对象编程
4.1 类与对象
面向对象的语言可以创建类,类是对事物的一种封装,如人,汽车,房子等都可以将它封装成一个类,类通常是名词,类中可以拥有自己的字段和函数,字段:该类拥有的属性,字段名通常是名词,函数:该类可以拥有的行为,函数名通常是动词。通过这种类的封装,我们可以在适当的时候创建该类的对象,调用对象中的字段和函数满足实际编程的要求。
package com.example.test1
class Person {
var name = ""
var age = 0
fun eat(){
println(name + "is eating . He is "+ age + "year old.")
}
}
对这个类进行实例化和一些操作
val p = Person()
p.name = "Jack"
p.age = 19
p.eat()
输出
4.2 继承与构造函数
4.2.1 继承
写一个Student类,继承Person类,这样Student类就自动拥有了Person中的字段和函数,另外还可以定义自己独有的字段和函数
class Student {
var sno = ""
var grade = 0
}
1. 使Person类可以被继承
在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final关键字。抽象类本身是无法创建实例的,一定要有子类去继承它才能创建实例,因此抽象类必须可以被继承。
加上open关键字
open class Person {
...
}
2. 继承,extends变成:
class Student : Person(){
var sno = ""
var grade = 0
}
4.2.2 构造函数
Kotlin将构造函数分为主构造函数和次构造函数
主构造函数:没有函数体,直接定义在类名的后面
class Student(val sno : String, val grade :Int):Person(){
}
//进行实例化:
val student = Student("a123",5)
主函数没有函数体,想在主构造函数中编写一些逻辑,init结构体:
class Student(val sno : String,val grade :Int):Person(){
init{
println("sno is "+sno);
println("grade is "+grade);
}
}
子类的构造函数必须调用父类中的构造函数,子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定。
上面代码Person类后面的()表示Student类的主构造函数在初始化的时候会调用Person类的无参构造函数
修改一下:
open class Person(val name:String,val age:Int) {
}
class Student(val sno:String, val grade:Int, name:String, age:Int):
Person(name,age){
}
注意:在主构造函数中声明成val或var的参数将自动成为该类的字段,会导致和父类中同名的name和age字段造成冲突,这里name,age前面不加任何关键字,让他的作用域仅限定在主构造函数中即可。
创建实例:
val student = Student("a123",5,"Jack",9)
次构造函数
- 任何一个类只能有一个主构造函数,但可以有多个次构造函数,次构造函数可以用于实例化一个类,有函数体。
- 当一个类既有主构造函数又有次构造函数时,所有次构造函数都必须调用主构造函数
- 次构造函数通过constructor关键字来定义
class Student(val sno:String, val grade:Int, name:String, age:Int):
Person(name,age){
constructor(name: String,age: Int):this("",0,name,age){ //通过this关键字调用了主构造函数
}
constructor():this("",0){ //调用了第一个次构造函数,间接调用了主构造函数
}
}
4.当类中只有次构造函数,没有主构造函数
class Student:Person{
constructor(name:String,age:Int):super(name,age){
}
}
没有主构造函数,继承Person类的时候不需要加上括号,次构造函数只能调用父类的构造函数,用了super关键字
4.3 接口
接口:用于实现多态编程的重要组成部分
Java是单继承结构语言,任何一个类最多只能继承一个父类,但是可以实现任意多个接口,可以在接口中定义一系列的抽象行为,然后由具体的类去实现
例子:
interface Study {
fun readBooks()
fun doHomework()
}
//让Student类去实现Study接口
class Student(name: String,age: Int):Person(name,age),Study{
override fun readBooks() {
println(name+" is reading")
}
override fun doHomework() {
println(name + " is doing homework")
}
}
//调用
fun main(){
val student = Student("Jack",19)
student.readBooks()
student.doHomework()
}
override关键字重写父类或者实现接口中的函数
这种叫做面向接口编程,也叫多态
也可以对接口中的函数进行默认实现
4.4 函数的可见性修饰符
4.5 数据类
数据类:将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持
数据类通常需要重写equals(),hashCode(),toString()这几个方法
data class Cellphone(val brand:String,val price:Double)
data:关键字,希望这个类是一个数据类,equals(),hashCode(),toString()等固定且无实际逻辑意义的方法自动生成
测试:
fun main(){
val cellphone1 = Cellphone("Samsung",1299.99)
val cellphone2 = Cellphone("Samsung",1299.99)
println(cellphone1)
println(cellphone1 == cellphone2)
}
4.6 单例类
单例模式:避免创建重复对象
Java:
public class Singleton {
private static Singleton instance;
private Singleton(){}
//禁止外部创建Singleton实例,private关键字将构造函数私有化
//给外部提供了一个getInstance()静态方法用于获取Singleton的实例
public synchronized static Singleton getInstance(){
if(instance!=null){
instance = new Singleton();
}
return instance;
}
public void singletonTest(){
System.out.println("SingleTonTest is called. ");
}
}
//要使用单例中的方法时
Singleton singleton = Singleton.getInstance();
singleton.singletonTest();
Kotlin:
object关键字
object Singleton {
fun singletonTest(){
println("SingleTonTest is called. ")
}
}
//调用
Singleton.singletonTest()
4.7 Lambda编程
简介
Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的 Java 代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。
JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。
对接口的要求
- Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法
- jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
- @FunctionalInterface修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。
4.7.1 集合的创建与遍历
List,Set,Map在Java中都是接口
List的主要实现类:ArrayList和LinkedList,Set:HashSet,Map:HashMap
listOf函数,创建的是一个不可变的集合,只能用于读取
val list = listOf("Apple","Banana","Orange","Pear")
for (fruit in list){
println(fruit)
}
mutableListOf()函数,创建的是可变集合
Set集合:setOf(),mutableSetOf(),注意Set集合底层是使用hash映射机制来存放数据的,集合中的元素无法保证有序,这是与List的不同
Map集合:
//写法1:
val map = HashMap<String,Int>()
map["Apple"] = 1
map["Banana"] = 2
map["Orange"] = 3
map["Pear"] = 4
map["Grape"] = 5
//写法2:
val map = mapOf("Apple" to 1,"Banana" to 2)
for((fruit,number) in map){
println("fruit is "+fruit+" , number is "+number)
}
4.7.2 集合的函数式 API
例子:找出长度最长的水果单词:
//普通写法
val list = listOf("Apple","Orange","Banana","Pear","Grape","Watermelon")
var maxLengthFruit = ""
for (fruit in list){
if(fruit.length > maxLengthFruit.length){
maxLengthFruit = fruit
}
}
println("max length fruit is "+maxLengthFruit)
//集合的函数式API
val list = listOf("Apple","Orange","Banana","Pear","Grape","Watermelon")
var maxLengthFruit = list.maxBy { it.length }
println("max length fruit is "+maxLengthFruit)
Lambda
Lambda就是一小段可以作为参数传递的代码
{ 参数名1: 参数类型 , 参数名2: 参数类型 -> 函数体 }
->符号表示参数列表的结束以及函数体的开始,函数体中可以编写任意行代码,最后一行代码会自动作为Lambda表达式的返回值
//maxBy,函数,接收的是一个Lambda类型的参数,
//遍历集合时将每次遍历的值作为参数传递给Lambda表达式
//工作原理:根据我们传入的条件来遍历集合,从而找到该条件下的最大值
//1.
val list = listOf("Apple","Orange","Banana","Pear","Grape","Watermelon")
val lambda = { fruit:String ->fruit.length }
val maxLengthFruit = list.maxBy (lambda)
//2.简化
val maxLengthFruit = list.maxBy({fruit:String -> fruit.length})
//3.简化——当Lambda参数是函数的最后一个参数时,将Lambda表达式移到函数括号的外面
val maxLengthFruit = list.maxBy(){fruit:String -> fruit.length}
//4.简化——当Lambda参数是函数的唯一一个参数时,将函数的括号省略
val maxLengthFruit = list.maxBy{fruit:String -> fruit.length}
//5.简化——类型推导机制
val maxLengthFruit = list.maxBy{fruit-> fruit.length}
//6.简化——当Lambda表达式的参数列表中只有一个参数时,不必声明参数名,可以使用it关键字来代替
val maxLengthFruit = list.maxBy{ it.length }
map函数
将集合的每个元素都映射成一个另外的值
val list = listOf("Apple","Orange","Banana","Pear","Grape","Watermelon")
val newList = list.map { it.toUpperCase() }
for (fruit in newList){
println(fruit)
}
filter函数
过滤集合中的数据
//保留5个字母以内的水果
val list = listOf("Apple","Orange","Banana","Pear","Grape","Watermelon")
val newList = list.filter { it.length <5 }
for (fruit in newList){
println(fruit)
}
any——集合中是否存在一个元素满足指定条件
all——集合中是否所有元素都满足指定条件
val list = listOf("Apple","Orange","Banana","Pear","Grape","Watermelon")
val anyResult = list.any { it.length<=5 }
val allResult = list.all { it.length <=5 }
4.7.3 Java函数式API的使用
在Kotlin中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口——接口中只有一个待实现方法
最常见的单抽象方法接口——Runnable接口
public interfac Runnable{
void run();
}
对于任何一个Java方法,只要它接收Runnable参数,就可以使用函数式API
例子:
Java版本:
new Thread(new Runnable(){
@Override
public void Run(){
System.out.println("Thread is running");
}
}).start();
Kotlin版本:
Thread(object : Runnable{
override fun run(){
println("Thread is running")
}
}).start()
//化简
Thread(Runnable {
println("Thread is running")
}).start()
//化简参数列表中有且仅有一个Java单抽象方法接口参数,将接口名省略
Thread( {
println("Thread is running")
}).start()
//最终化简
Thread{
println("Thread is running")
}.start()
注意:本节中的Java函数式API使用都限定从Kotlin中调用Java方法,并且单抽象方法接口也必须是用Java语言定义的
4.8 空指针检查
4.8.1 可空类型
Kotlin默认所有的参数和变量都不可为空
为空的类型系统:在类名后面加上问号
fun main(){
doStudy(null)
}
fun doStudy(study: Study?){
if(study!=null){
study.doHomework()
study.readBooks()
}
}
4.8.2 判空辅助工具
?.操作符
if(a!=null){
a.doSomething()
}
即
a?.doSomething()
?: 操作符
val c = if(a!=null){
a
}else{
b
}
即
val c = a?:b
辅助工具——let
是一个函数,这个函数提供了函数式API的编程接口
而且可以处理全局变量的判空问题,if是无法解决的
fun doStudy(study:Study?){
study?.let{ stu -> //对象为空时什么都不做,不为空时调用let函数
//let函数将study对象本身作为参数传递到Lambda表达式中
//study的对象肯定不为空
stu.readBooks()
stu.doHomework()
}
}
4.9 字符串内嵌表达式——${}
当表达式中仅有一个变量的时候,将两边的大括号省略
fun main(){
doStudy(null)
val name = "Tome"
val age = 19
println("name $name ,age $age")
}
4.10 函数的参数默认值
可以给函数设定参数默认值
printParams(str = "world",name = 123)
//不管哪个参数在前还是在后,都可以准确将参数匹配上