百日学 Swift(Day 10) – classes and inheritance(类和继承)
1. Creating your own classes(创建自定义类) – test
类和结构体有 5 个区别。
区别 1
:类永远不会带有成员初始化器。这意味着,如果类中有不带默认值的属性,则必须始终创建初始化程序对这些
属性初始化。而结构体则不需要。
初始化器 initializer 在其他语言中或者称为 构造函数
2. Class inheritance(类的继承) – test
区别 2
:类可以继承,结构体不能。被继承的称为“父类”或“超类”,继承的类称为“子类”。子类拥有父类所有的属性和方法,还可以添加自己的属性和方法。
出于安全原因,初始化子类时总是要调用super.init()
——以防万一父类在创建时会做一些重要的工作。**但要放在对子类的属性初始化之后。**父类属性不能直接先用 self 方式初始化,但在 super.init()
之后可以使用 self 方式修改父类属性值。
class Person { // 父类
var name: String // 父类属性
var age: Int
var description: String {"\(name) + \(age)"} // 父类计算属性
init(name: String, age: Int) { // 父类的初始化器
self.name = name
self.age = age
}
}
class Teacher: Person { // 子类 继承 父类 Person
var lesson: String // 子类的属性
var school: String = "雨燕中学" // 带默认值的属性
init(name: String, age: Int, lesson: String) { // 子类的初始化
// 初始化子类属性,
// 一定要放在 super.init 之前,否则报错:
// Property 'self.param' not initialized at implicitly generated super.init call
self.lesson = lesson
// 调用父类初始化器,初始化父类属性
// 如果只是使用 self.父类属性 进行初始化,一样会报错
super.init(name: name, age: age)
}
}
let t1 = Teacher(name: "张三", age: 41, lesson: "英语")
print("我是 \(t1.name), \(t1.age)岁. 我在 \(t1.school) 教 \(t1.lesson) 。")
print("t1.decription : \(t1.description).") // 计算属性也被赋值了
// 输出结果:空格是为了更好的显示属性的使用。
我是 张三, 41岁. 我在 雨燕中学 教 英语。
t1.decription : 张三 + 41.
记住:在初始化器或者声明属性的时候都可以为属性提供一个
默认值
。
3. Overriding methods(覆写方法) – test
子类可以使用自己的实现替换父类的方法,这称为 覆写 overriding
。
class Person { // 父类
var name: String // 父类属性
var age: Int
func ability() { // 父类方法
print("我会吃喝拉撒睡")
}
init(name: String, age: Int) { // 父类初始化
self.name = name
self.age = age
}
}
class Teacher: Person { // 子类
var lesson: String // 子类属性
var school: String = "雨燕中学"
override func ability() { // 覆写父类方法
print("我会教书.")
}
func ability(lesson: String) { // 重载
print("我会教\(lesson).")
}
init(name: String, age: Int, lesson: String) { // 子类初始化
self.lesson = lesson
super.init(name: name, age: age)
}
}
let t1 = Teacher(name: "张三", age: 31, lesson: "英语")
print("我是\(t1.name), 我\(t1.age)岁.")
t1.ability()
t1.ability(lesson: t1.lesson)
print("我在\(t1.school)教\(t1.lesson).")
// 运行结果:
我是张三, 我31岁.
我会教书.
我会教英语.
我在雨燕中学教英语.
覆写和重载的区别:
- 覆写 overriding :同方法名,同参数名,同参数类型,同参数个数,同返回类型
- 重载 overloading :同样的方法名、参数名、参数类型,参数个数和返回类型不同
4. Final classes(最终类) – test
在 class
关键字前面加上 final
关键字,会将类声明为 final 类,任何其他类都不能继承该类。
5. Copying objects(复制对象) – test
区别 3
:类的对象复制,复制副本和原始副本指向同一个对象,改变其中一个会影响到另一个。而结构体则是分别指向两个对象。
class Person {
var name: String = "张三"
}
var p1 = Person()
print("=== 复制前 ===")
print("p1.name : \(p1.name)")
print("=== 复制后 ===")
var p1Copy = p1 // 复制 类 对象
p1Copy.name = "李四"
print("p1.name : \(p1.name)")
print("p1Copy.name : \(p1Copy.name)")
// 运行结果
=== 复制前 ===
p1.name : 张三
=== 复制后 ===
p1.name : 李四
p1Copy.name : 李四
struct Person {
var name: String = "张三"
}
var p1 = Person()
print("=== 复制前 ===")
print("p1.name : \(p1.name)")
print("=== 复制后 ===")
var p1Copy = p1 // 复制 结构体 对象
p1Copy.name = "李四"
print("p1.name : \(p1.name)")
print("p1Copy.name : \(p1Copy.name)")
// 运行结果
=== 复制前 ===
p1.name : 张三
=== 复制后 ===
p1.name : 张三
p1Copy.name : 李四
6. Deinitializers(析构器) – test
区别 4
:类可以有反初始化器,有的语言可能称之为析构函数,这个函数在销毁类的实例的时候被调用。
class Person {
var name = "张三"
init() { // 初始化器,构造函数
print("创建一个对象:\(self)")
}
deinit { // 反初始化器,析构函数
print("\(self) 被销毁")
}
func printGreeting() {
print("Hello, I'm \(name)")
}
}
for _ in 1...3 {
let person = Person()
person.printGreeting()
}
// 运行结果
创建一个对象:__lldb_expr_43.Person
Hello, I'm 张三
__lldb_expr_43.Person 被销毁
创建一个对象:__lldb_expr_43.Person
Hello, I'm 张三
__lldb_expr_43.Person 被销毁
创建一个对象:__lldb_expr_43.Person
Hello, I'm 张三
__lldb_expr_43.Person 被销毁
7. Mutability(可变性) – test
区别 5
:对于使用 let 声明的常量,如果是类,可以修改类中的变量属性;如果是结构体,则不能修改其中的变量属性(要想修改必须用var
声明)。
class Singer {
var name = "Taylor Swift"
}
let taylor = Singer() // taylor 为常量……
taylor.name = "Ed Sheeran" // taylor 的变量属性 name 被修改了……
print(taylor.name)
8. Classes summary(类小结) – test
- 类和结构体相似,它们都可以使用属性和方法创建自定义类型。
- 一个类可以从另一个类继承,并获得父类的所有属性和方法。谈论类层次结构是很常见的——一个类基于另一个,而另一个类本身又基于另一个。(一个类不能继承多个类,全是单线联系的地下党)
- 可以使用
final
关键字标记一个类,这将阻止其他类从该类继承。- 通过方法重写,子类可以使用新的实现替换其父类中的方法。
- 当两个变量指向同一类实例时,它们都指向同一块内存——改变一个会改变另一个。
- 类可以具有一个反初始化器,该反初始化器是在销毁该类的实例时运行的代码。
- 类并不像构造结构体那样强烈地强制执行常量——如果将类属性声明为变量,则无论是否将类实例声明为常量,都可以对其进行更改。