@@ -204,6 +204,20 @@ var weight = 70.5 // double
204204 }
205205 ```
206206
207+
208+
209+ ### 变量保存了指向对象的引用
210+
211+ ![ Image] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/variable.jpg?raw=true )
212+
213+ 当该对象被赋值给变量时,这个对象本身并不会被直接赋值给当前的变量。相反,该对象的引用会被赋值给该变量。因为当前的变量存储的是对象的引用,因此它可以访问该对象。
214+
215+ 如果你使用val来声明一个变量,那么该变量所存储的对象的引用将不可修改。然而如果你使用var声明了一个变量,你可以对该变量重新赋值。例如,如果我们使用代码: ` x = 6 ` ,将x的值赋为6,此时会创建一个值为6的新Int对象,并且x会存放该对象的引用。下面新的引用会替代原有的引用值被存放在x中:
216+
217+ ![ Image] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/var_chage.jpg?raw=true )
218+
219+ ** 注意: 在Java中,数字类型是原生类型,所以变量存储的是实际数值。但是在Kotlin中的数字也是对象,而变量仅仅存储该数字对象的引用,并非对象本身。**
220+
207221### 优先使用val来避免副作用
208222
209223在很多Kotlin的学习资料中,都会传递一个原则:优先使用val来声明变量。这相当正确,但更好的理解可以是:尽可能采用val、不可变对象及纯函数来设计程序。关于纯函数的概念,其实就是没有副作用的函数,具备引用透明性。
@@ -267,7 +281,7 @@ var size: Int = 2
267281这个例子中就会内存溢出。
268282
269283` kotlin ` 为此提供了一种我们要说的后端变量,也就是` field ` 。编译器会检查函数体,如果使用到了它,就会生成一个后端变量,否则就不会生成。
270- 我们在使用的时候,用` field ` 代替属性本身进行操作。按照惯例` set ` 参数的名称是` value ` ,但是如果你喜欢你可以选择一个不同的名称。
284+ 我们在使用的时候,用` field ` 代替属性本身进行操作。按照惯例` set ` 参数的名称是` value ` ,但是如果你喜欢你可以选择一个不同的名称。setter通过field标识更新变量属性值。field指的是属性的支持字段,你可以将其视为对属性的底层值的引用。在getter和setter中使用field代替属性名称很重要,因为这样可以阻止你陷入无限循环中。
271285
272286``` kotlin
273287class A {
@@ -289,7 +303,17 @@ fun main() {
2893031
290304```
291305
306+ 如果我们不手动写getter和setter方法,编译器会在编译代码时添加以下代码段:
307+
308+ ``` kotlin
309+ var myProperty: String
310+ get() = field
311+ set(value) {
312+ field = value
313+ }
314+ ```
292315
316+ 这意味着无论何时当你使用点操作符来获取或设置属性值时,实际上你总是调用了属性的getter或是setter。那么,为什么编译器要这么做呢?为属性添加getter和setter意味着有访问该属性的标准方法。getter处理获取值的所有请求,而setter处理所有属性值设置的请求。因此,如果你想要改变处理这些请求的方式,你可以在不破坏任何人代码的前提下进行。通过将其包装在getter和setter中来输出对属性的直接访问称为数据隐藏。
293317
294318## 延迟初始化
295319
@@ -375,37 +399,88 @@ val sex: String by lazy(LazyThreadSafetyMode.NONE) {
375399
376400## 类的定义:使用` class ` 关键字
377401
402+ 当你在定义类的时候,你需要想想该类所创建的对象需要什么。你需要考虑:
403+
404+ - 每个对象自身的特点
405+
406+ 对象自身的特点称为属性(properties)。它们代表了对象自身的状态(数据),并且该类中的每一个对象都有自己独特的数值。例如,一个狗(Dog)类可能有名字(name)、体重(weight)和品种(breed)属性。一个歌曲(Song)类可能有标题(title)和演唱者(artist)属性。
407+
408+ - 每个对象的行为
409+
410+ 对象的行为是它们的函数(functions)。它们决定了对象的行为,并且可能回使用对象的属性。例如,之前提到的Dog类,可能具有吠叫(bark)函数;Song这个类可能会有播放(play)函数。
411+
412+
413+
378414类可以包含:
415+
379416- 构造函数和初始化块
380417- 函数
381418- 属性
382419- 嵌套类和内部类
383420- 对象声明
384421
385422
386- ``` kotlin
387- class MainActivity {
388423
424+ 你可以将类想象成一个对象的模板,因为它告诉编译器如何创建该特定类的对象。它还将告诉编译器每个对象应该具有哪些属性,并且从该类生成的每个对象都可以拥有自己独有的属性值。例如,每个Dog对象都有自己的名称、重量和品种属性,每个Dog的属性值都可以是不同的。
425+
426+ ![ Image] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/kotlin_class_1.jpg?raw=true )
427+
428+
429+
430+
431+ ``` kotlin
432+ class Dog (val name : String , var weight : Int , val breed : String ){
433+ fun bark () {
434+
435+ }
389436}
390437```
391438
392439如果有参数的话你只需要在类名后面写上它的参数,如果这个类没有任何内容可以省略大括号:
393440``` kotlin
394- class Person ( name : String , age : Int )
441+ class Dog ( val name : String , var weight : Int , val breed : String )
395442```
396443
397444### 创建类的实例
398445
399446``` kotlin
400- val person = Person ( " charon " , 18 )
447+ val myDog = Dog ( " Fido " , 70 , " Mixed " )
401448```
402449
403450上面的类有一个默认的构造函数。
404451
405452注意:创建类的实例不用` new ` 了啊。
406453
454+ ![ Image] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/kotlin_class_dog_sample.jpg?raw=true )
455+
456+ ![ Image] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/kotlin_class_dog.jpg?raw=true )
457+
458+ 类中所定义的函数又称为成员函数。有时也被称为方法。
459+
460+ #### 创建对象的执行过程
461+
462+ ``` kotlin
463+ var myDog = Dog (" Fido" , 70 , " Mixed" )
464+ ```
465+
466+ 1 . 系统会为每个传入Dog构造函数的参数创建一个对象。它会创建一个值为“Fido”的String,一个值为70的Int,以及一个值为“Mixed”的String。
467+ 2 . 系统会为一个新的Dog对象分配空间,并且Dog构造函数会被调用。
468+ 3 . Dog构造函数定义了三个属性:名称、重量以及品种。在这个现象背后,每一个属性实际上是一个变量。对于构造函数中定义的每个属性,都会有一个相应类型的变量被创建。
469+ 4 . 相应的变量的引用将会被赋值给Dog的属性。例如,值为“Fido”的String将会被赋值给name属性。
470+ 5 . 最后,这个新的Dog对象的引用将会被赋值给名为myDog的Dog变量。
471+
472+ ![ Image] ( https://raw.githubusercontent.com/CharonChui/Pictures/master/kotlin_dog_new.jpg?raw=true )
473+
474+
475+
476+
477+
478+
479+
407480### 构造函数
408481
482+ 构造函数包含了初始化对象所需的代码。它在对象被分配给引用之前运行,这意味着你有机会对对象进行一些内部操作以便其被使用。大多人使用构造函数来定义对象的属性,并且给这些属性赋值。每当你创建一个新的对象,该对象所属的类的构造函数将会被调用。构造函数在你初始化对象时被调用。它通常被用于定义对象的属性,并且对属性赋值。
483+
409484在` Kotlin ` 中的一个类可以有一个主构造函数和一个或多个次构造函数。
410485
411486#### 主构造函数
@@ -616,6 +691,8 @@ data class Artist(
616691 var mbid : String )
617692```
618693
694+ 数据类自动覆盖它们的equals方法以改变==操作符的行为,由此通过检查对象的每个属性值来判断是否相等。例如,假设你创建了两个属性值完全相同的Recipe对象,使用==操作符对它们进行比较将返回true,因为它们存放了相同的数据:除了提供从Any父类继承的equals方法的新实现,数据类还覆盖了hashCode和toString方法。
695+
619696通过数据类,会自动提供以下函数:
620697
621698- 所有属性的` get() set() ` 方法
@@ -649,7 +726,13 @@ val charon2 = charon.copy(age = 19)
649726- data class之前不能用abstract、open、sealed或者inner进行修饰
650727- 在Kotlin 1.1版本前数据类只允许实现接口,之后的版本既可以实现接口也可以继承类
651728
652- ## 多声明
729+
730+
731+ 与任何其他类一样,你可以向数据类添加属性和方法,只需要将它们包含在类主体中。但是有一个大问题。在编译器生成数据类的方法实现时,比如覆盖equals方法和创建copy方法,它仅包含在主构造函数中定义的属性。因此如果你在数据类主体中定义添加的属性,则它们不会被包含到任何编译器生成的方法中。
732+
733+ ### 数据类定义了componentN方法
734+
735+ 定义数据类时,编译器会自动向该类添加一组方法,你可以将其作为访问对象属性值的替代方法。它们被称为componentN方法,其中N表示被访问属性的编号(按声明排序)。
653736
654737多声明,也可以理解为变量映射,这就是编译器自动生成的` componentN() ` 方法。
655738
@@ -689,6 +772,10 @@ open class Person(num: Int)
689772class SuperPerson (num : Int ) : Person(num)
690773```
691774
775+ 冒号后面的Person(num)会调用Person类的构造函数,以确保所有的初始化代码(例如给属性赋值)能够被执行。调用父类构造函数是强制性的:如果父类有主构造函数,你必须在子类头中调用它,否则代码将无法通过编译。请记住,即使你没有在父类中显式地添加构造函数,编译器也会在编译代码的时候自动创建一个空构造函数。假如我们不想为Person类添加构造函数,因此编译器在编译代码的时候创建了一个空构造函数。该构造函数通过使用Person()被调用。
776+
777+
778+
692779如果该类有一个主构造函数,其基类必须用基类型的主构造函数参数就地初始化。
693780如果类没有主构造函数,那么每个次构造函数必须使用` super ` 关键字初始化其基类型,或委托给另一个构造函数做到这一点。
694781注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:
@@ -849,7 +936,7 @@ open class SuperPerson(num: Int) : Person(num) {
849936}
850937```
851938
852- 每个声明的属性可以由具有初始化器的属性或者具有` get ` 方法的属性覆盖,你也可以用一个 ` var ` 属性覆盖一个 ` val ` 属性,但反之则不行 。
939+ 每个声明的属性可以由具有初始化器的属性或者具有` get ` 方法的属性覆盖,如果某个属性在父类中被定义为val,你可以在子类中使用var属性覆盖它。只需要覆盖该属性并将其声明为var即可。请注意,这只适用于这一种方式。如果尝试使用val覆盖var属性,编译器将会感到沮丧并拒绝编译你的代码 。
853940
854941
855942
@@ -886,9 +973,10 @@ abstract class Derived : Base() {
886973
887974
888975
889-
890976## 接口:使用` interface ` 关键字
891977
978+ 接口可以让你在父类层次结构之外定义共同的行为接口用于为共同行为定义协议,使你可以不依赖严格的继承结构却又可以利用多态。与抽象类类似,接口不能被实例化且可以定义抽象或具体的方法和属性,但两者有一个关键的不同点:类可以实现多个接口,但是只能继承于一个直接父类。所以接口不仅拥有抽象类的优点,而且使用起来更加灵活。
979+
892980``` kotlin
893981interface FlyingAnimal {
894982 fun fly ()
@@ -964,6 +1052,26 @@ toast("Hello")
9641052toast(" Hello" , Toast .LENGTH_LONG )
9651053```
9661054
1055+ ### 无参主函数
1056+
1057+ 如果你使用的是Kotlin1.2或更早的版本,若想正常运行程序,你的主函数必须写成如下形式:
1058+
1059+ ``` kotlin
1060+ fun main (args : Array <String >) {
1061+ // ...
1062+ }
1063+ ```
1064+
1065+ 从Kotlin1.3版本器,你可以忽略main函数的参数,写成如下形式:
1066+
1067+ ``` kotlin
1068+ fun main () {
1069+ // ...
1070+ }
1071+ ```
1072+
1073+
1074+
9671075
9681076
9691077### 可变长参数函数:使用` vararg ` 关键字
@@ -981,6 +1089,16 @@ fun main(args: Array<String>) {
9811089}
9821090```
9831091
1092+ 如果你有一个现有的值数组,则可以通过在数组名前加上` * ` 来将这些值传递给该函数。星号` (*) ` 被称为扩展运算符,以下是它的一些使用示例:
1093+
1094+ ``` kotlin
1095+ vval myArray = arrayOf(1 , 2 , 3 , 4 , 5 )
1096+ val mList = vars(* myArray)
1097+ val mList2 = vars(0 , * myArray, 6 , 7 )
1098+ ```
1099+
1100+
1101+
9841102
9851103
9861104### ` Unit ` :让函数调用皆为表达式
0 commit comments