《第一行代码》 第三版 - 第二章(笔记)

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对象数据类型数据类型
intInt整型
longLong长整型
shortShort短整型
floatFloat单精度浮点型
doubleDouble双精度浮点型
booleanBoolean布尔型
charChar字符型
byteByte字节型

在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修饰符

修饰符JavaKotlin
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")
//正是这种方法,所以,主构造函数添加多个参数,不同使用方法,都同样使用主构造函数来创建对象


这篇关于《第一行代码》 第三版 - 第二章(笔记)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程