面向对象特征-继承
# 前言
面向对象的三大特征:
封装 (
Encapsulation
)继承 (
Inheritance
)多态 (
Polymorphism
)
接下来我们学习继承。
# 什么是继承?
使用extends
关键字来继承另一个类
语法格式
class Subclass extends SuperClass{
}
2
3
- Subclass:新建的类(正式名称叫作继承类或者子类)
- extends:关键字
- SuperClass:原始类(正式名称叫作基类、超类或父类)
# 为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类或超类)。可以理解为:“子类is a 父类”。
在创建了一个类之后,即使另一个新类与其具有相似的功能,你还是得重新创建一个新类。但我们若能利用现成的数据类型,对其进行“克隆”,再根据情况进行添加和修改,情况就显得理想多了。“继承”正是针对这个目标而设计的。但继承并不完全等价于克隆。在继承过程中,若原始类(正式名称叫作基类、超类或父类)发生了变化,修改过的“克隆”类(正式名称叫作继承类或者子类)也会反映出这种变化。
作用:
- 继承的出现减少了代码冗余,提高了代码的复用性
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
# 如何使用继承?
- 子类继承了父类,就继承了父类的方法和属性
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的属性和方法。
- 使用格式:
对象.属性
或者对象.方法
- 在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
class Person{
private int age;
private String name;
//省略setter和getter
}
class Student extends Person{
//新的属性
private int score;
//新的方法
public void Study(){
System.out.println("学习...");
}
//省略setter和getter
}
Class Test{
public static void main(String[] args){
Student stu = new Student();
stu.setAge(14);//继承了父类的属性和方法,可以直接使用。
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
注意:
- 子类不能直接
访问/调用
父类中私有的(private
)的成员变量和方法。虽然不能访问,但是还是可以继承父类中的所有成员变量和方法。
# "是一个"与"像是一个"的关系 (opens new window)
对于继承可能会引发争论:继承应该只覆盖基类的方法(不应该添加基类中没有的方法)吗?如果这样的话,基类和派生类就是相同的类型了,因为它们具有相同的接口。这会造成,你可以用一个派生类对象完全替代基类对象,这叫作"纯粹替代",也经常被称作"替代原则"。在某种意义上,这是一种处理继承的理想方式。我们经常把这种基类和派生类的关系称为是一个(
is-a
)关系,因为可以说"圆是一个形状"。判断是否继承,就看在你的类之间有无这种 is-a 关系。有时你在派生类添加了新的接口元素,从而扩展接口。虽然新类型仍然可以替代基类,但是这种替代不完美,原因在于基类无法访问新添加的方法。这种关系称为像是一个(
is-like-a
)关系。新类型不但拥有旧类型的接口,而且包含其他方法,所以不能说新旧类型完全相同
# 什么时候该使用继承?
利用is-a
判断, 该不该使用继承关系。
比如学生是人,那么Student 可以继承 Person。
比如dog is a Animal,那么可以使用继承关系。
比如储蓄卡是一种银行卡,那么可以使用继承关系。
比如cat is dog?那么就不应该使用继承关系。
当然,不单只考虑is-a
来判断,还要根据实际业务场景来判断,具体问题具体分析,我上面说是可以继承,而不是一定要继承,要分清楚。
Java只支持单继承和多层继承,不允许多重继承
一个子类只能有一个父类。
一个父类可以派生出多个子类。
# 单继承与多层继承举例
多层继承,后面继承的类拥有前面所有继承的属性和方法。
# 单继承结构 (opens new window)
所有的类都直接或间接继承Object
类
如果没有显式声明一个类的父类的话,则此类直接继承object
类
自从 C++ 引入以来,一个 OOP 问题变得尤为突出:是否所有的类都应该默认从一个基类继承呢?这个答案在 Java 中是肯定的(实际上,除 C++ 以外的几乎所有OOP语言中也是这样)。在 Java 中,这个最终基类的名字就是
Object
。Java 的单继承结构有很多好处。由于所有对象都具有一个公共接口,因此它们最终都属于同一个基类。相反的,对于 C++ 所使用的多继承的方案则是不保证所有的对象都属于同一个基类。从向后兼容的角度看,多继承的方案更符合 C 的模型,而且受限较少。
对于完全面向对象编程,我们必须要构建自己的层次结构,以提供与其他 OOP 语言同样的便利。我们经常会使用到新的类库和不兼容的接口。为了整合它们而花费大气力(有可能还要用上多继承)以获得 C++ 样的“灵活性”值得吗?如果从零开始,Java 这样的替代方案会是更好的选择。
另外,单继承的结构使得垃圾收集器的实现更为容易。这也是 Java 在 C++ 基础上的根本改进之一。
由于运行期的类型信息会存在于所有对象中,所以我们永远不会遇到判断不了对象类型的情况。这对于系统级操作尤其重要,例如异常处理 (opens new window)。同时,这也让我们的编程具有更大的灵活性。