# 继承

在前面的课程中,你已经看到了几次提到的 继承 。在 Java 语言中,类可以从其他类派生,从而继承这些类的字段和方法。

# 定义

从另一个类派生的类称为子类(派生类,扩展类或子类)。派生子类的类称为超类(也是基类或父类)。

除了 Object 没有超类,每个类都有一个且只有一个直接超类(单继承)。在没有任何其他显式超类的情况下,每个类都是 Object 隐式的子类。

类可以派生自派生自类的类的类,等等,最终派生自最上面的 Object 类 。这样的类被认为是下降的所有继承链可以追溯到 Object 类。

继承的想法很简单,但功能强大:当你想创建一个新的类,并且已经有一个包含你想要的代码的类时,你可以从现有的类中派生出新的类。在这样做的时候,你可以重用现有类的字段和方法,而不必自己编写(和调试)它们。

子类从其父类继承所有成员(字段,方法和嵌套类)。构造函数不是成员,所以它们不会被子类继承,但是可以从子类中调用超类的构造函数。

# Java 平台类层次结构

Object 在 java.lang 包中定义的 ,类定义并实现了所有类共同的行为,包括你编写的类。在 Java 平台中,许多类直接派生自其他类,派生自其中一些类,等等,形成类的层次结构。

Java 平台中的所有类都是 Object 的后代

在层次结构的顶部,Object 是所有类中最通用的。靠近层次结构底部的类提供更专业化的行为。

# 一个继承的例子

public class Bicycle {

    // the Bicycle class has three fields
    public int cadence;
    public int gear;
    public int speed;

    // the Bicycle class has one constructor
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }

    // the Bicycle class has four methods
    public void setCadence(int newValue) {
        cadence = newValue;
    }

    public void setGear(int newValue) {
        gear = newValue;
    }

    public void applyBrake(int decrement) {
        speed -= decrement;
    }

    public void speedUp(int increment) {
        speed += increment;
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

Bicycle 的子类 MountainBike 可能如下所示:

public class MountainBike extends Bicycle {

    // the MountainBike subclass adds one field
    public int seatHeight;

    // the MountainBike subclass has one constructor
    public MountainBike(int startHeight,
                        int startCadence,
                        int startSpeed,
                        int startGear) {
        super(startCadence, startSpeed, startGear);
        seatHeight = startHeight;
    }   

    // the MountainBike subclass adds one method
    public void setHeight(int newValue) {
        seatHeight = newValue;
    }   
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

MountainBike 继承 Bicycle 所有的字段和方法,并添加 seatHeight 字段和方法来设置它。除了构造函数外,就好像你已经从头开始写了一个新的类,有四个字段和五个方法。但是,你并不需要做所有的工作。如果 Bicycle 类中的方法复杂且需要大量的时间进行调试,这将是特别有价值的。

# 你可以在子类中做什么

子类继承父类的所有 public 和 protected 成员,不管子类是什么包。如果子类与其父类位于同一个包中,它也继承了父类的包 private 成员,您可以按原样使用继承的成员,替换它们,隐藏它们,或者用新成员补充它们:

  • 继承的字段可以直接使用,就像其他字段一样。
  • 您可以在子类中声明一个与超类中的字段相同的字段,从而隐藏它(不推荐)。
  • 您可以在子类中声明不在超类中的新字段。
  • 继承的方法可以直接使用。
  • 您可以在与超类中签名相同的子类中编写一个新的 实例 方法,从而 覆盖 它。
  • 你可以在子类中写一个新的 静态 方法,它与超类中的签名相同,从而 隐藏 它。
  • 您可以在子类中声明不在超类中的新方法。
  • 你可以编写一个调用超类的构造函数的子类构造函数,或者隐式地使用 super 关键字。

本课的以下部分将讲述这些主题。

# 父类的私有成员

子类不会继承其父类的 private 成员,但是,如果超类具有访问其私有字段的公共或受保护的方法,则这些也可以由子类使用。

# 对象

我们已经看到,一个对象是它实例化的类的数据类型。例如,如果我们写

public MountainBike myBike = new MountainBike();
1

那么 myBike 的类型是 MountainBike。

MountainBike 是 Bicycle 和 Object 的子类,因此,MountainBike 是一个 Bicycle,也是一个 Object,并且它可用于任何要求 Bicycle 或 Object 对象的地方。

反过来并不一定是正确的: Bicycle 可能是 MountainBike,但不一定。同样,一个 Object 可能是一个 Bicycle 或一个 MountainBike,但不一定。

在继承和实现所允许的对象之中,Casting 显示使用一种类型的对象来代替另一种类型的对象。例如,如果我们写

Object obj = new MountainBike();
1

然后 obj 是一个 Object 和一个 MountainBike(直到 obj 被分配的另一个对象不是一个 MountainBike)。这被称为 隐式投射

如果,另一方面,我们写

MountainBike myBike = obj;
1

我们会得到一个编译时错误,因为编译器不知道 obj 是一个 MountainBike。但是,我们可以告诉编译器,我们承诺通过显式的转换来分配一个 MountainBiketo 给 obj

MountainBike myBike = (MountainBike)obj;
1

这个转换插入了一个运行时检查,这个检查 obj 被分配了一个 MountainBike,这样编译器就可以安全地假定它 obj 是一个 MountainBike。如果在运行时 obj 不是 MountainBike,则会抛出异常。

TIP

您可以使用 instanceof 运算符对特定对象的类型进行逻辑测试。这可以避免由于不正确的转换造成的运行时错误。例如:

if (obj instanceof MountainBike) {
    MountainBike myBike = (MountainBike)obj;
}
1
2
3

在这里 instanceof 验证 obj 是否是 MountainBike,这样我们就可以知道将不会抛出运行时异常。