文档

Java™教程
隐藏目录
继承
路径:学习Java语言
课程:接口和继承

继承

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


定义: 从另一个类派生的类称为子类(也称为派生类扩展类子类)。子类从其派生的类称为超类(也称为基类父类)。

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

类可以从派生的类派生的类派生的类,依此类推,并且最终从顶级类Object派生。这样的类被称为从继承链中的所有类中派生Object

继承的思想简单而强大:当你想要创建一个新类,并且已经存在一个包含你想要的一些代码的类时,你可以从现有类派生出你的新类。通过这样做,你可以重用现有类的字段和方法,而无需自己编写(和调试!)它们。

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

Java平台类层次结构

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

Java平台中的所有类都是Object的子类

Java平台中的所有类都是Object的子类

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

继承的示例

下面是一个可能的实现一个Bicycle类的示例代码,它在“类和对象”课程中进行了介绍:

public class Bicycle {
        
    // Bicycle类有三个字段
    public int cadence;
    public int gear;
    public int speed;
        
    // Bicycle类有一个构造函数
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }
        
    // Bicycle类有四个方法
    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 {
        
    // MountainBike子类添加一个字段
    public int seatHeight;

    // MountainBike子类有一个构造函数
    public MountainBike(int startHeight,
                        int startCadence,
                        int startSpeed,
                        int startGear) {
        super(startCadence, startSpeed, startGear);
        seatHeight = startHeight;
    }   
        
    // MountainBike子类添加一个方法
    public void setHeight(int newValue) {
        seatHeight = newValue;
    }   
}

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

子类可以做什么

子类继承了其父类的所有公共受保护的成员,无论子类在哪个包中。如果子类与其父类在同一个包中,它还会继承父类的包私有成员。你可以直接使用继承的成员,替换它们,隐藏它们,或者补充它们的新成员:

本课程的以下章节将详细介绍这些主题。

父类中的私有成员

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

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

对象的类型转换

我们已经了解到,对象的数据类型是其实例化的类的类型。例如,如果我们写下:

public MountainBike myBike = new MountainBike();

那么myBike的类型就是MountainBike

MountainBike是从BicycleObject继承而来的。因此,MountainBike既是Bicycle,也是Object,它可以在需要BicycleObject对象的任何地方使用。

反过来则不一定成立:一个Bicycle可能是一个MountainBike,但也可能不是。同样,一个Object可能是一个BicycleMountainBike,但也可能不是。

类型转换是在继承和实现允许的对象类型之间使用一个类型的对象来替代另一个类型的对象。例如,如果我们写下:

Object obj = new MountainBike();

那么obj既是一个Object,也是一个MountainBike(直到obj被赋予另一个不是MountainBike的对象为止)。这被称为隐式类型转换

然而,如果我们写下:

MountainBike myBike = obj;

我们将得到一个编译时错误,因为编译器不知道obj是一个MountainBike。然而,我们可以通过显式类型转换告诉编译器,我们承诺将一个MountainBike赋给obj

MountainBike myBike = (MountainBike)obj;

这个类型转换会在运行时检查obj是否被赋予了一个MountainBike,以便编译器可以安全地假设obj是一个MountainBike。如果运行时obj不是一个MountainBike,就会抛出异常。


注意: 您可以使用instanceof操作符对特定对象的类型进行逻辑测试。这可以避免由于错误的类型转换而导致运行时错误。例如:
if (obj instanceof MountainBike) {
    MountainBike myBike = (MountainBike)obj;
}

这里的instanceof操作符验证obj是否引用了一个MountainBike,以便我们可以在进行转换时知道不会抛出运行时异常。



上一页:问题和练习:接口
下一页:状态、实现和类型的多重继承