《第一行代码》 第三版 - 第二章(笔记)
2021/10/14 23:14:48
本文主要是介绍《第一行代码》 第三版 - 第二章(笔记),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
探究新语言,快速入门Kotlin编程
1.Kotlin
1.1Kotlin在Android的地位
在Android诞生以来,一直都是只提供Java这一种语言来开发应用程序的
在2017年的I/O大会上,Google宣布了Kotlin作为Android的一级开发语言,和Java平起平坐
在2019年的I/O大会上,Google宣布,Kotlin成为第一开发语言,当然Java开发依然有用
至今,在国外的安卓市场上,已经有绝大多数的App已经在使用Kotlin开发了。
而Google的一些官方视频和开源项目都是使用Kotlin,Kotlin也愈发的重要。
1.2Kotlin的历史进程
2011年 JetBrains 公布了第一个版本的Kotlin
2012年Kotlin开源
2016年 Kotlin 发布了1.0正式版,同时IDEA也支持Kotlin的使用
2017年 Google宣布Kotlin成为Android的一级语言
2019年 Google宣布Kotlin成为Android的第一语言
1.3编程语言
编程语言 | 作用 | 语言 |
---|---|---|
编程型语言 | 编译器将我们编写的源代码一次性编译成计算机可识别的二进制,直接运行 | C、C++ |
解释型语言 | 解释器会一行行读取源代码,实时将这些源代码解释成二进制数据,再去执行,效率会差点 | Python、JavaScript |
2变量与函数
2.1变量
Kotlin与Java的变量有很大的区别
Java:整型变量(int a)、字符串变量(String b)
Kotlin定义变量只有两种关键字声明方式
val(value简写) 用于声明不可变变量,即初始赋值之后,再也无法修改变量,对应Java的final变量
var(variable简写) 用于声明可变变量,即初始赋值之后,还可以改变变量的内容。对应Java的非final变量
在Kotlin写完一行代码后,是不需要分号 ;的,而Java是需要的,所以Java改Kotlin时,需要注意一下
var a = 10 //自动声明为int类型数据 var b = "你好啊" //自动声明为字符串类型数据
在Kotlin中什么类型的数据都是使用var或val来定义变量的,因为它会自动识别这是什么类型的数据,这是Kotlin的类型推导机制,但是如果我们的变量是延迟赋值的,他就无法自动推导,这时就需要我们显式的声明变量的类型是什么
//当显式声明了变量,Kotlin推导机制就不会推导该变量,当你尝试赋值一个String字符串给a,那么会抛出异常 var a:Int = 10
Java基本数据类型 | Kotlin对象数据类型 | 数据类型 |
---|---|---|
int | Int | 整型 |
long | Long | 长整型 |
short | Short | 短整型 |
float | Float | 单精度浮点型 |
double | Double | 双精度浮点型 |
boolean | Boolean | 布尔型 |
char | Char | 字符型 |
byte | Byte | 字节型 |
在Java中很少人会主动去使用final,这就导致,后期并不知道这个变量在哪里被修改了,所以导致提高排插成本。而在Kotlin中,一开始定义变量的时候就必须定义是var还是val,主动声明,变量是否可以改变。
郭婶的说法就是,优先使用val(不可变变量),在你后面需要改变变量的时候,将val改成var
2.2函数
方法与函数
方法和函数其实没有什么区别,只是叫法不一样,是从英文翻译过来的function、method。在Java中叫的多的是方法、在Kotlin中叫的多的是函数
//使用fun关键字定义函数 //methodName就是定义函数名 //(括号内的就是接受的参数,任意数量(可为空),此例子就是两个Int类型的参数) //格式:参数名(随便取): 参数类型 //()后的 :Int 就是声明该函数要返回的是Int类型的数据 //{}内是函数体 fun methodName(param1: Int, param2: Int): Int{ //标准函数 return 0 }
语法糖
Kotlin允许我们不必编写函数体,可以直接将唯一一行的代码写在函数定义的尾部,中间使用等号连接
//原函数 fun largerNumber(num1: Int, num2: Int): Int{ return max(num1, num2) } //返回的是一个整型数据,而这个返回的整型数据是传入max函数后,max函数返回的一个整型数据,因为只有一行,所以允许不写函数体 fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)
3.程序的逻辑控制
程序的执行语句分3种:顺序语句、条件语句和循环语句
顺序语句:顾名思义就是,按着顺序一行一行的执行代码
条件语句只有两种,一种是if语句,另一种是when语句
循环语句:顾名思义就是,循环执行某一段代码,有两种循环语句,一个是while循环另一个是for循环
3.1if条件语句
fun largerNumber(num1: Int, num2: Int): Int{ var value = 0; if(num1 > num2){ //如果num1 大于 num2就获取num1 value = num1 } else { //否则就获取num2 value = num2 } return value //返回获取到的数,其实就是返回最大的数 } //简化写法 fun largerNumber(nuum1: 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 } //最简洁的写法 fun largerNumber(num1: Int, num2: Int) = if(num1 > num2) num1 else num2
3.2when条件语句
与Java中的switch类似,但他不需要每个case后需要加break,只需写需要的逻辑。
whe语句允许传入任意类型的参数,可以在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语句可以进行类型匹配
//Number类型,是Int、Long、Float、Double等与数字相关的类都是它的子类 fun checkNumber(num: Number) { when (num) { //is关键字相当于Java的instanceof is Int -> println("is Int") is Double -> println("is Double") else -> println("not support") } }
不传参数的使用方法
//有时候可能需要单独将表达式放入判断,适用于单独判断 fun getScore1(name: String) = when { name.startsWith("Tom") -> 86 //名字以Tom开头的 name == "Jim" -> 77 name == "Jack" -> 95 name == "Lily" -> 100 else -> 0 }
3.3循环语句
while方法和Java一样,略
//当while(布尔表达式)的布尔表达式为false则退出循环 //而while与do... while最大的区别是,while先判断表达式再决定是否循环,而do....while则是先执行一次之后才判断表达式再决定是否循环,所以最少也是要循环一次 fun getWhile() { var i = 10 while (i > 0) { println("i is $i") i -= 1 } var y = 10 do { println("y is $y") y -= 1 } while (y > 0) }
for循环语句
Kotlin的循环语句就只有 for - in循环
比for - i 简单,但没for-i那样灵活
//区间:并且是包括0和10的,数学表达式 : [0,10] val range = 0..10 //区间,左闭右开,包含0,不包含10.数学表达式: [0,10) val range = 0 until 10 fun forIn(){ //输出0 - 10 for (i in 0..10){ println(i) } //输出0-9 for (i in 0 until 10){ println(i) } //循环0-9的数,但每次增加2 //step关键字,相当于Java中的i = i+2 for (i in 0 until 10 step 2){ println(i) } //倒叙,10开始降到1,downTo关键字,可以配合step使用 for (i in 10 downTo 1){ print(i) } }
4面向对象编程
4.1面向对象、类与对象
Kotlin和Java一样,也是面向对象语言
面向对象是可以创建类的,类就是对一种事或物的封装,将它们封装成一个类,类名通常都是名词,而类里面也有自己的字段和函数,字段是所拥有的属性,而函数是所拥有的行为。
类与对象
对象其实就是类的一个实例对象,简单来说就是,我定义了一个"小柴"类(这是一种动物)
"小柴"这种动物有年龄 age 和 名字name, 也有行为,他会吃饭eat()函数,还会睡觉sleep()函数
然后我定义一个"小柴"实例对象,也就是我有了一只小柴这种动物的宠物,他的名字叫Memory,他刚出生没多久,只有1岁,他会吃饭eat(),也会睡觉sleep()
4.2 继承与构造函数
在Kotlin中,类是默认不可继承的,若是需要被某个类继承某,这个类必须前面加上open。这样子类才可以使用父类的方法和变量
//被继承的类 open class Person{ ...... } //继承的类 class Student : Person(){ //这里的被继承的类是要加上()的,因为这里涉及了主构造函数,主构造和次构造函数与Java的有点不一样 }
Java中的构造函数
public class Student{ public Student(){ } public Student(int age, String name){ } }
而Kotlin的比较特殊,分主构造函数和次构造函数
主构造函数将会是你最常用的构造函数,每个类都会默认一个不带参数的主构造函数,你可以显示的指明他的参数。而主构造函数的特点是没有函数体,直接定义在类名的后面既可
//这里的Person()其实就是在调用Person的构造方法Person() class Student(val sno: String, val grade: Int) : Person(){ //init构造体,主构造函数的逻辑都可以写在这里面(但这不是最好的办法,一般都不会这样去使用) init{ println("sno is " + sno) println("grade is "+ grade) } }
次构造函数
你几乎用不到的次构造函数,Kotlin提供了一个函数设定参数默认值的功能,基本上可以替代次构造函数的作用。一个类只能有一个主构造函数和多个次构造函数,次构造函数一样可以实例化一个类,但他是有函数体的。(也就是说,你想少某一个变量,在主构造函数设置默认值既可,设置之后,你使用主构造函数创建的时候,可以不用添加该变量)
class Student(var){ }
当一个类既有主构造函数,又有次构造函数时,所有的次构造函数必须调用主构造函数(包括间接调用)
//这里的age和name,不能设置为val,是因为会和父类的age和name发生冲突,不加他们作用域仅在主构造函数内 class Student(val sno:String, val grade: Int, name: String, age: Int) :Person(name, age) { //通过constructor关键字来定义次构造函数 constructor(name: String, age: Int):this("", 0, name, age){ } constructor() : this("", 0){ } } //这是在没有主构造函数情况下的次构造函数的创建,没有主构造函数,所以继承Person时不需要加括号 class Student : Person{ constructor(name:String, age: Int) : super(name, age){ } }
4.3 接口
接口与Java的几乎差不多
一个类可以实现多个接口
//接口 interface Study{ fun readBooks() fun doHomework() } //类实现接口,使用的是用逗号分开 class Student(name: String, age: Int) : Person(name, age), Study{ //override关键字重写或实现接口函数 override fun readBooks(){ println(name + " is reading.") } override fun doHomework(){ println(name + " is doing homework.") } } //接口2 interface Study{ fun readBooks() fun doHomework() { //这种情况下,实现Study接口时,可以不实现doHomework()方法,不实现,就是该方法有效,重写就以重写的方法为准 println(name + " is doing homework.") } }
4.4修饰符
修饰符 | Java | Kotlin |
---|---|---|
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类、子类、同一包路径下的类可见 | 当前类、子类可见 |
default | 同一包路径下的类可见(默认) | 无 |
protected | 无 | 同一模块中的类可见 |
4.5 数据类和单例类
在Java中,数据类通常需要重写equals()、hashCode()、toString()方法
而Kotlin实现很简单
//使用了data关键字时,就会根据主构造函数的参数,帮你自动生成equals()、hashCode()、toString()方法 data class Person(val country: String, val sex: String)
在Java中创建单例的方法
//单例的创建 public class Singleton{ private static Singleton instance; private 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中创建单例的方法
//就这么简单,这就是一个单例模式 //不需要提供任何方法去创建,就将class关键字改成object关键字既可,因为Kotlin自动会帮你创建一个Singleton实例,保证全局只有一个实例 object Singleton{ //在单例模式中添加一个函数 fun singletonTest(){ println("singletonTest is called.") } } //单例的使用方法,类似于Java中的静态方法的调用方法 Singleton.singletonTest()
4.6Lambda编程
集合主要分3种: List、Set和Map
List 主要实现类是ArrayList和LinkedList
Set主要实现类是HashSet
Map主要实现类是HashMap
//创建一个ArrayList实例 val list = ArrayList<String>() list.add("Apple") list.add("Banana") list.add("Pear") //使用listOf()函数来简化初始化写法 //但是listOf创建的集合是不可变的集合,也就是创建之后,就不能再改变,只能读取,不能改变 val list = listOf("Apple", "Banana", "Pear") //循环 for(fruit in list){ println(fruit) } //这是可以修改的list初始化方法 val list = mutableListOf("Apple", "Banana", "Pear") list.add("Orange") //Set的用法和List几乎一样,就只是改成setOf()和mutableSetOf() val set = setOf("Apple", "Banana", "Pear") val set = mutableSetOf("Apple", "Banana", "Pear") //Map的用法和List与Set的用法有比较大的区别 //Map的第一种用法 val map = HashMap<String, Int>() map.put("Apple", 1) map.put("Banana", 2) map.put("Pear", 3) //Map的第二种用法 val map = HashMap<String, Int>() map["Apple"] = 1 map["Banana"] = 2 map["Pear"] = 3 //Map的最简单用法 val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4) //循环 for((fruit, number) in map){ prinltln("fruit is " + fruit + ", number is " + number) }
集合的函数式API
val list = listOf("Apple", "Banana", "Pear") val maxLengthFruit = list.maxBy{it.length} //获取最长的水果
Lambda表达式:
{参数名1,: 参数类型,参数名2: 参数类型 -> 函数体}
Lambda表达式,可以编辑任意行的代码,但是不建议太长,,最后一行会自动作为Lambda表达式的返回值
上边的就是一个代码段,只是因为Kotlin的特性,所以就缩减到了一句话
val list = listOf("Apple", "Banana", "Pear") val lambda = {fruit: String -> fruit.length} val maxLengthFruit = list.maxBy{lambda} //获取最长的水果 //简化版 val maxLengthFruit = list.maxBy({fruit: String -> fruit.length}) //Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到括号的外面 val maxLengthFruit = list.maxBy() {fruit: String -> fruit.length} //Lambda参数是函数的唯一参数,可以省略括号 val maxLengthFruit = list.maxBy{fruit: String -> fruit.length} //Kotlin推导机制,可以省略String类型声明 val maxLengthFruit = list.maxBy{fruit-> fruit.length} //当Lambda表达式只有一个参数时,可以用it关键字代替 val maxLengthFruit = list.maxBy{it.length}
map函数,将集合中的元素映射成另外的值,而规则是在Lambda中规定
val newList = list.map{"代码块"}
filter函数,时用于过滤集合中的元素,条件就是Lambda中的代码
//保留5个字符以内的水果 val newList = list.filter{ it.length <= 5}
any函数,只要集合中有一个元素满足,就会返回true,如果都不满足才返回false
all函数,只有集合中的所有元素都满足,才会返回true,否则只要有一个不满足就会返回false
//只要有一个水果的长度小于5就返回true val newList = list.any{ it.length <= 5} //只要有一个水果不小于5就返回false val newList = list.all{ it.length <= 5}
4.7 在Kotlin中使用Java的函数式API
在Kotlin中调用Java方法,并且该方法接受一个Java单抽象方法接口参数,就可以使用函数式API
单抽象方法是指只有该接口中只有一个待实现的方法
例子:Runnable接口
//Runnable接口 public interface Runnable{ void run(); } //使用Runnable接口的用法 new Thread(new Runnable(){ @Override public void run(){ System.out.println("Thread is running"); } }).start();
//Kotlin使用Runnable接口的用法 //使用object关键字创建匿名类实力 Thread(object : Runnable{ override fun run(){ println("Tread is running") } }).start() //函数式API的简写方法 //当只有一个待实现办法,Kotlin能自动明白是里面的Lambda表达式就是run()方法的实现内容 Thread(Runnable{ println("Thread is running") }).start() //当Java方法的参数列表不存在一个以上的Java单抽象方法接口参数,我们还可以将接口名进行省略 Thread({ println("Thread is running") }).start() //Lambda表达式是方法的最后一个参数时,可以将Lambda表达式移到括号外,同时表达式是唯一一个参数是可以省略括号 Thread{ println("Thread is running") }.start();
虽然现在我们都是使用Kotlin写代码,但我们需要经常和Android SDK打交道,而它还是使用Java编写的。例如Button的监听事件
button.setOnClickListener{ }
4.8 空指针
相信写Android,或多或少都有解除过空指针异常,在Java中,在出现有可能是空数据的时候,我们都要进行判空。而Kotlin则有一个判空机制,就是利用编译的时候判空检查,这样就几乎阻止空指针异常。
但这样可能会使代码写的比较麻烦,不过Kotlin提供了一套工具,可以让我们轻松判空
///这种情况下,你传一个空,编译的时候就会报错,因为出现了空指针 doStudy(null) //传空 fun doStudy(study:Study){ study.readBooks() study.doHomework() }
这样的话,我们只要传空就会报错,但有时候,我们又需要传空,那这是我们就需要问号了,在数据类型后面加上?就代表该数据是可以传空的
Int? 代表可空的整型数据 String? 表示可空的数据,
//这个时候,传空就不会报错 doStudy(null) //传空 fun doStudy(study:Study?){ study.readBooks() study.doHomework() }
虽然可以传空了,但里面的两个方法肯定会报空指针异常,因为他们是必须要有数据才可以执行,这时候我们就可以想Java 那样写了
fun doStudy(study:Study){ if(study != null){ study.readBooks() study.doHomework() } }
但是这样写的话,就没什么优势了,而且还很麻烦
这里就可以用到?. 这个符号的意思代表着,当对象不空的时候调用,对象为空的时候,就不执行
相当于Java的这一段代码
if(a != null){ a.doSomething() }
在Kotlin中就可以这样使用?.符号来简化代码
//a是否有对象? 有,就执行doSomething()方法,没有就跳过不执行 a?.doSomething()
?:操作符,该操作符两边都是表达式,则如果表达式的结果不为空就返回左边的表达式的结果,否则就返回右边表达式的结果。
//如果a不为空,则c = a; 否则 c = b val c = a ?: b //原写法 fun getTextLength(text: String?): Int{ if(text != null){ return text.length } return 0 } //使用?. 与?:的写法 //当text为空的时候,不执行length方法,返回一个null,再使用?:判断右边是不是null,是就返回一个0, fun getTextLength(text: String?) = text?.length ?: 0
虽然有空指针检查机制,但是有时候他也有失败的时候
fun main(){ if(content != null){ printUpperCase() } } //虽然在外面的时候已经判断空了,但在方法里面的时候会重新判断该变量有没有可能是空,因为方法是不知道外边主函数里已经进行了判空,只能自己判断是否为空 fun printUpperCase(){ val upperCase = content.toUpperCase() } //可使用强行编译 !!. 符号,但这种写法是有风险的,因为要我们自己保证这里是不是不会空,否则会报错闪退 val upperCase = content!!.toUpperCase()
let函数,使用方法,这里是调用了obj对象的let函数,然后Lambda表达式中的代码会理科执行,并且将obj对象传进表达式中,这里的obj 与obj2其实是同一个对象。该函数可以与判空机制检查配合使用
obj.let{ obj2 -> //编写具体代码 } //原 代码 //这其实等同于,两次if(study != null){}来判断,每一个?.都判断一次 fun doStudy(study: Study?){ study?.readBooks() study?.doHomework() } //配合let使用,这代码的意思就是,只要study不为空就执行let函数 //而let函数就将study对象传递进Lambda表达式 fun doStudy(study: Study?){ study?.let{ stu -> stu.readBooks() stu.doHomework() } } //简化 fun doStudy(study: Study?){ study?.let{ it.readBooks() it.doHomework() } }
当study对象是全局变量时,if语句还是会报错,比如
var study: Study? = null fun doStudy(){ if(study != null){ study.readBooks() study.doHomework() } }
但let函数可以处理全局判空的问题
4.9 字符串内嵌表达式
Kotlin字符串内嵌表达式
// ${} 表达式,可以踢到一部分内容 "hello, ${obj.name}. nice to meet you!" //当只有一个变量的时候,还可以省略{} "hello, $name . nice to meet you!" //println("my name is " + name + ", my age is " + age) //println("my name is $name, my age is $age")
4.10函数的参数默认值
Kotlin提供了函数设定参数默认值的功能
正因为这个功能,所以次构造函数使用的情况很少
fun printParams(num: Int, str: String = "hello"){ println("num is $num, str is $str") } //上面的函数第二个参数是默认值,所以我们使用的时候,可以只传一个Int参数 fun main(){ printParams(12345) } //当给第一个设定默认值的时候 fun printParams(num: Int = 100, str: String){ println("num is $num, str is $str") } //错误的使用,这会报错的 printParams("hello") //那是不是不能这样用了,不是的,Kotlin机制还有一个通过键值对的方式来传参 printParams(str = "world", num = 123) printParams(str = "world") //正是这种方法,所以,主构造函数添加多个参数,不同使用方法,都同样使用主构造函数来创建对象
这篇关于《第一行代码》 第三版 - 第二章(笔记)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南