Inheritance

在前面的类中,您多次看到继承。在 Java 语言中,类可以从其他类派生,从而从这些类继承字段和方法。

Definitions:

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

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

可以从派生自类的类派生类,这些类派生自类,依此类推,final派生自最顶层的类Object。据说这样的类是继承链中所有类的后代,并延伸回Object

继承的概念很简单但是很强大:当您要创建一个新类并且已经有一个包含所需代码的类时,可以从现有类中派生新类。这样,您可以重用现有类的字段和方法,而不必自己编写(和调试!)它们。

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

Java 平台类层次结构

java.lang程序包中定义的Object类定义并实现了所有类(包括您编写的类)共有的行为。在 Java 平台中,许多类直接从Object派生,其他类从其中一些类派生,依此类推,形成了类的层次结构。

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

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

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

继承的示例

以下是“类和对象”类中介绍的Bicycle类的可能实现的示例代码:

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;
    }
        
}

MountainBike类的Bicycle子类的类声明可能看起来像这样:

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;
    }   
}

MountainBike继承Bicycle的所有字段和方法,并添加字段seatHeight和设置它的方法。除了构造函数外,就好像您完全从头开始编写了一个新的MountainBike类,具有四个字段和五个方法。但是,您不必完成所有工作。如果Bicycle类中的方法很复杂并且调试花费了大量时间,那么这将特别有价值。

您可以在子类别中做什么

子类继承其父级的所有公共成员和受保护成员,无论该子类位于哪个程序包中。如果该子类与其父级位于同一程序包中,则它也继承父级的程序包私有成员。您可以按原样使用继承的成员,替换它们,隐藏它们,或用新成员补充它们:

  • 继承的字段可以像其他任何字段一样直接使用。

  • 您可以在子类中声明一个与超类中的名称相同的字段,从而将其隐藏(不建议)。

  • 您可以在子类中声明不在超类中的新字段。

  • 继承的方法可以直接使用。

  • 您可以在子类中编写一个新的实例方法,该方法具有与超类中的签名相同的签名,从而将其覆盖。

  • 您可以在子类中编写一个新的静态方法,该方法具有与超类中的签名相同的签名,从而将其隐藏。

  • 您可以在子类中声明不在超类中的新方法。

  • 您可以编写一个隐式或使用关键字super调用超类的构造函数的子类构造函数。

本课的以下各节将对这些主题进行扩展。

超类的私人成员

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

嵌套类可以访问其封闭类的所有私有成员,包括字段和方法。因此,子类继承的公共或受保护的嵌套类可以间接访问超类的所有私有成员。

Casting Objects

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

public MountainBike myBike = new MountainBike();

那么myBike的类型为MountainBike

MountainBikeBicycleObject的后代。因此,MountainBikeBicycle也是Object,并且可以在需要BicycleObject对象的任何地方使用。

相反的情况不一定成立:Bicycle可以是MountainBike,但不一定。类似地,Object可以是BicycleMountainBike,但不一定。

在继承和实现允许的对象中,强制转换显示了使用一种类型的对象代替另一种类型的对象。例如,如果我们写

Object obj = new MountainBike();

那么obj既是Object又是MountainBike(直到obj被分配了另一个不是MountainBike的对象)。这称为隐式转换。

另一方面,如果我们写

MountainBike myBike = obj;

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

MountainBike myBike = (MountainBike)obj;

此强制转换插入一个运行时检查,确定obj被分配了MountainBike,以便编译器可以安全地假定objMountainBike。如果obj在运行时不是MountainBike,则将引发异常。

Note:

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

if (obj instanceof MountainBike) {
MountainBike myBike = (MountainBike)obj;
}

在这里instanceof运算符验证obj引用了MountainBike,以便我们可以在不抛出运行时异常的情况下进行强制转换。