这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
5.2、继承
在前面的教程中,我们多次提到过 继承 。在Java编程语言中,一个类可以从其他的类派生出来,从而继承这些类的字段和方法。
继承 的思想很简单但是非常强大:当你想创建一个新类,但已知有一个类存在一部分你想要的代码,那么你就可以从这个已经存在的类中派生出新的类。这样做,你就可以重用这个类的字段和方法,而不用自己再写一遍。
子类将会继承父类中的所有成员(字段、方法和嵌套类)。构造器不是成员,所以构造器不会被继承,但可以在子类中调用父类的构造器。
Java平台类的层次结构
java.lang package包中定义的 Object 类。定义并实现了所有类的通用行为,在Java平台中,有很多类直接从 Object 类派生而来,另外一些类从其他的类派生而来,如此,就形成了类的层次结构。
在层次结构的最顶端,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类中的方法非常复杂,并且花费了大量时间进行调试,那么这将更有价值。
你可以在子类中做什么
子类继承了父类所有public和protected修饰的成员,而不管子类处于哪个包下。如果子类和父类在同一个包下,它将继承父类的package-private成员。你可以按原样使用继承的成员,替换它们,隐藏它们,或者用新成员补充它们:
- 继承的字段可以直接使用,就跟其他字段一样
- 可以在子类中定义一个与父类中字段同名的字段,称为 隐藏(不推荐)
- 可以在子类中声明父类中不存在的字段
- 继承的方法可以直接使用
- 可以在子类中声明一个与父类中签名相同的实例方法,称为重写
- 可以在子类中声明一个与父类中签名相同的静态方法,称为隐藏
- 可以在子类中声明父类中不存在的新方法
- 可以在子类构造器中调用父类构造器,隐式调用或者使用关键字super
本课程后续的章节将对以上情形展开讨论。
父类中的私有成员
子类不会继承父类中的private成员。然而,如果父类有提供public和protected修饰的用来访问private字段的方法,这些方法依然可以被子类使用。
嵌套类可以访问闭包类中的所有私有成员。因此,子类继承的public或protected嵌套类可以间接访问父类的所有私有成员。
对象转型
我们知道一个对象是实例化该对象的类的类型。比如:
public MountainBike myBike = new MountainBike();
这表示myBike是MountainBike类型的。
MountainBike是由Bicycle和Object演化而来的。也就是说,一个MountainBike是一个Bicycle,同时也是一个Object。它可以用在任何可以使用Bicycle和Object的地方。
反过来就不对了:一个Bycicle可能是一个MountainBike,但不是说一定是。同样,一个Object可能是Bicycle或者MountainBike,但不是说一定是。
转型是指用一种类型的对象代表另外一种类型的对象,当然这些对象必须是有继承与实现关系的。比如:
Object obj = new MountainBike();
此时obj既是一个Object也是一个MountainBike。这被称为隐式转换。
另一方面,如果:
MountainBike myBike = obj;
我们将获得一个编译错误,因为对编译器而言它不知道obj是MountainBike类型的。然而,我们可以向编译器保证传递一个MountainBike类型的对象给到obj。如下:
MountainBike myBike = (MountainBike)obj;
该转型引入了一个运行时检查,obj被赋值给MountainBike类型,因此编译器假设obj是MountainBike类型的。如果在运行时obj不是MountainBike类型的,将会抛出一个异常。