多态
多态
多态的词典定义指的是生物学中的一种原理,即生物体或物种可以具有多种不同的形式或阶段。这一原理也可以应用于面向对象编程和像 Java 语言这样的语言。类的子类可以定义自己的独特行为,同时共享父类的一些相同功能。
可以通过对 Bicycle
类进行一些小的修改来演示多态。例如,可以向该类添加一个 printDescription()
方法,该方法显示当前存储在实例中的所有数据。
public void printDescription(){
System.out.println("\nBike is " + "in gear " + this.gear
+ " with a cadence of " + this.cadence +
" and travelling at a speed of " + this.speed + ". ");
}
为了演示 Java 语言中的多态特性,用 MountainBike
和 RoadBike
类扩展 Bicycle
类。对于 MountainBike
,添加一个用于悬挂的字段,该字段是一个 String
值,表示自行车是否具有前减震器,Front
。或者,自行车具有前后减震器,Dual
。
以下是更新后的类
public class MountainBike extends Bicycle {
private String suspension;
public MountainBike(
int startCadence,
int startSpeed,
int startGear,
String suspensionType){
super(startCadence,
startSpeed,
startGear);
this.setSuspension(suspensionType);
}
public String getSuspension(){
return this.suspension;
}
public void setSuspension(String suspensionType) {
this.suspension = suspensionType;
}
public void printDescription() {
super.printDescription();
System.out.println("The " + "MountainBike has a" +
getSuspension() + " suspension.");
}
}
请注意重写的 printDescription()
方法。除了之前提供的信息之外,还将有关悬挂的附加数据包含在输出中。
接下来,创建 RoadBike
类。由于公路自行车或赛车自行车具有细轮胎,因此添加一个属性来跟踪轮胎宽度。以下是 RoadBike
类
public class RoadBike extends Bicycle{
// In millimeters (mm)
private int tireWidth;
public RoadBike(int startCadence,
int startSpeed,
int startGear,
int newTireWidth){
super(startCadence,
startSpeed,
startGear);
this.setTireWidth(newTireWidth);
}
public int getTireWidth(){
return this.tireWidth;
}
public void setTireWidth(int newTireWidth){
this.tireWidth = newTireWidth;
}
public void printDescription(){
super.printDescription();
System.out.println("The RoadBike" + " has " + getTireWidth() +
" MM tires.");
}
}
请注意,printDescription()
方法再次被重写。这次,将显示有关轮胎宽度的信息。
总而言之,有三个类:Bicycle
、MountainBike
和 RoadBike
。这两个子类重写了 printDescription()
方法并打印独特的信息。
以下是一个测试程序,它创建三个 Bicycle
变量。每个变量都分配给三个自行车类之一。然后打印每个变量。
public class TestBikes {
public static void main(String[] args){
Bicycle bike01, bike02, bike03;
bike01 = new Bicycle(20, 10, 1);
bike02 = new MountainBike(20, 10, 5, "Dual");
bike03 = new RoadBike(40, 20, 8, 23);
bike01.printDescription();
bike02.printDescription();
bike03.printDescription();
}
}
以下是测试程序的输出
Bike is in gear 1 with a cadence of 20 and travelling at a speed of 10.
Bike is in gear 5 with a cadence of 20 and travelling at a speed of 10.
The MountainBike has a Dual suspension.
Bike is in gear 8 with a cadence of 40 and travelling at a speed of 20.
The RoadBike has 23 MM tires.
Java 虚拟机 (JVM) 会调用与每个变量中引用的对象相对应的适当方法。它不会调用由变量类型定义的方法。这种行为被称为虚拟方法调用,它展示了 Java 语言中重要多态特性的一个方面。
隐藏字段
在一个类中,与超类中字段同名的字段会隐藏超类的字段,即使它们的类型不同。在子类中,无法通过其简单名称引用超类中的字段。相反,必须通过 super
访问该字段,这将在下一节中介绍。一般来说,我们不建议隐藏字段,因为它会使代码难以阅读。
使用关键字 super
访问超类成员
如果您的方法重写了其超类中的某个方法,则可以通过使用关键字 super
来调用重写的方法。您还可以使用 super
来引用隐藏的字段(尽管不建议隐藏字段)。请考虑以下类 Superclass
public class Superclass {
public void printMethod() {
System.out.println("Printed in Superclass.");
}
}
以下是一个子类,名为 Subclass
,它重写了 printMethod()
public class Subclass extends Superclass {
// overrides printMethod in Superclass
public void printMethod() {
super.printMethod();
System.out.println("Printed in Subclass");
}
public static void main(String[] args) {
Subclass s = new Subclass();
s.printMethod();
}
}
在 Subclass
中,简单名称 printMethod()
指的是在 Subclass
中声明的名称,它重写了 Superclass
中的名称。因此,要引用从 Superclass
继承的 printMethod()
,Subclass
必须使用限定名称,使用 super
,如所示。编译和执行 Subclass
会打印以下内容
Printed in Superclass.
Printed in Subclass
子类构造函数
以下示例说明了如何使用 super 关键字来调用超类的构造函数。回想一下 Bicycle
示例中 MountainBike
是 Bicycle
的子类。以下是 MountainBike
(子类)构造函数,它调用超类构造函数,然后添加自己的初始化代码
public MountainBike(int startHeight,
int startCadence,
int startSpeed,
int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
调用超类构造函数必须是子类构造函数中的第一行。
调用超类构造函数的语法为
super();
或
super(parameter list);
使用 super()
,将调用超类的无参数构造函数。使用 super(parameter list)
,将调用具有匹配参数列表的超类构造函数。
注意:如果构造函数没有显式调用超类构造函数,则 Java 编译器会自动插入对超类无参数构造函数的调用。如果超类没有无参数构造函数,您将收到编译时错误。
Object
确实有这样的构造函数,因此如果Object
是唯一的超类,则不会出现问题。
如果子类构造函数显式或隐式调用了其超类的构造函数,您可能会认为会有一系列构造函数被调用,一直追溯到 Object
的构造函数。事实上,情况确实如此。这被称为构造函数链,在存在很长的类继承链时,您需要了解它。
编写 final 类和方法
您可以声明一个类中的一些或所有方法为 final。您在方法声明中使用 final
关键字来指示该方法不能被子类重写。Object
类就是这样做的——它的许多方法都是 final 的。
如果某个方法的实现不应该更改,并且它对对象的持续状态至关重要,您可能希望将其设为 final。例如,您可能希望将此 ChessAlgorithm
类中的 getFirstPlayer()
方法设为 final
class ChessAlgorithm {
enum ChessPlayer { WHITE, BLACK }
...
final ChessPlayer getFirstPlayer() {
return ChessPlayer.WHITE;
}
...
}
从构造函数调用的方法通常应该声明为 final。如果构造函数调用非 final 方法,则子类可能会重新定义该方法,从而导致意外或不希望的结果。
请注意,您还可以声明整个类为 final。声明为 final 的类不能被子类化。这在创建不可变类(例如 String
类)时特别有用。
最后更新时间: 2021 年 9 月 14 日