10.1.3 继承与泛化的基本概念
继承和泛化描述的是一般与特殊的关系。它们让子类复用父类已有的数据和方法,也允许子类在父类基础上增加或修改自己的特征。
泛化与特化
泛化是从多个特殊类中抽取共同特征,形成更一般的父类。特化则是从一般类出发,得到更具体的子类。
| 方向 | 含义 | 例子 |
|---|---|---|
| 泛化 | 从特殊到一般,抽共同点 | 从“猫类、狗类”抽象出“动物类” |
| 特化 | 从一般到特殊,细化差异 | 从“动物类”派生出“猫类、狗类” |
在 UML 中,泛化关系常用空心三角箭头表示,箭头指向更一般的父类。
继承是什么
继承是子类获得父类属性和方法的机制。父类中定义的共同特征,子类可以直接复用;子类还可以新增自己的属性和方法,或重新实现父类方法。
| 术语 | 含义 |
|---|---|
| 父类/超类/基类 | 更一般的类 |
| 子类/派生类 | 更具体的类 |
| 继承 | 子类获得父类特征 |
| 复用 | 子类直接使用父类已有数据和方法 |
| 扩展 | 子类增加新的属性或方法 |
| 覆盖/重写 | 子类重新实现父类已有方法 |
继承不是复制出完全一样的类。子类和父类有共同点,也有自己的差异。
单重继承与多重继承
| 类型 | 含义 | 语言例子 | 风险 |
|---|---|---|---|
| 单重继承 | 一个子类只有一个直接父类 | Java 类继承通常是单重继承 | 结构清晰 |
| 多重继承 | 一个子类可以有多个直接父类 | C++ 支持多重继承 | 可能出现二义性 |
多重继承的问题在于:如果两个父类有同名成员,子类到底继承哪一个,可能产生二义性。因此一些语言用接口等机制替代直接多重继承。
抽象方法与重写
父类有时只能定义“应该有这个行为”,但不知道具体怎么实现。例如父类定义 call() 方法,不同子类有不同实现。
java
abstract class Animal {
abstract void call();
}
class Cat extends Animal {
void call() { System.out.println("meow"); }
}
class Dog extends Animal {
void call() { System.out.println("woof"); }
}父类中没有具体实现的方法称为抽象方法。子类继承后提供自己的实现,这就是重写/覆盖。
重写与重载
重写和重载非常容易混淆。
| 概念 | 发生位置 | 判断标准 | 例子 |
|---|---|---|---|
| 重写/覆盖 | 父子类之间 | 子类用同名、同参数方法替换父类实现 | Cat.call() 重写 Animal.call() |
| 重载 | 同一个类中 | 方法同名,但参数列表不同 | print(int) 与 print(String) |
重写强调“继承后修改父类方法”;重载强调“同名方法因参数不同而区分”。
例题
“学生是人”在 UML 类图中常表达为:
同一个类中存在多个同名方法,但参数列表不同,这通常称为:
本节小结
泛化是从特殊到一般,继承是子类获得父类特征。子类可以复用、扩展、重写父类内容。单重继承结构清晰,多重继承可能带来二义性。重写发生在父子类之间,重载发生在同一个类内,二者要严格区分。