方法重写(运行时)
本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 29 课,共 87 课。
方法重载在编译时确定,而方法重写体现了运行时多态。在这里,Java 会在程序实际运行时,根据对象的实际类型而不是变量的声明类型,决定执行哪个方法。
你在继承章中学习了方法重写。Java 处理父类引用指向子类对象的情况时,体现出了多态性:
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}现在观察一下使用父类型引用时会发生什么:
Animal myPet = new Dog();
myPet.speak(); // 输出:Bark!
myPet = new Cat();
myPet.speak(); // 输出:Meow!虽然 myPet 声明为 Animal,但 Java 会在运行时根据对象的实际类型调用重写的方法。这称为动态方法分派。JVM 会查看对象的真实类型,而不是变量声明的类型。
这样就能实现强大的模式,例如统一处理不同的对象:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // 每个对象都会调用自己的版本
}运行时多态让你能够编写灵活的代码,使用父类型,同时自动采用正确的子类型行为。
挑战
简单让我们构建一个通知系统,演示运行时多态如何发挥作用。你将创建一个通知类型的层次结构,在运行时,同一个方法调用会根据对象的实际类型产生不同的输出。
你将把代码分成 4 个文件:
Notification.java:创建所有通知共用的基类。每个通知都有一个recipient字段(String)。添加一个用于初始化该字段的构造函数、一个 getter 方法,以及一个会打印以下内容的send()方法:Sending notification to [recipient]EmailNotification.java:创建一个继承自 Notification 的类。电子邮件通知会添加一个subject字段。使用super初始化父类。重写send()方法,使其打印:Emailing [recipient]: [subject]SMSNotification.java:创建另一个继承自 Notification 的类。SMS 通知会添加一个phoneNumber字段。重写send(),使其打印:Texting [phoneNumber] for [recipient]Main.java:运行时多态将在这里大显身手!你会收到 3 个输入:收件人姓名、电子邮件主题和电话号码。创建一个包含 3 个对象的Notification引用数组,顺序依次为:一个基础 Notification、一个 EmailNotification 和一个 SMSNotification(都使用同一个收件人)。然后遍历数组,并对每个元素调用send()。看看 Java 如何根据每个对象的实际类型,自动调用正确的重写版本!
你会收到 3 个输入:收件人姓名(String)、电子邮件主题(String)和电话号码(String)。
你的输出应包含 3 行。即使你是在 Notification 引用上调用同一个方法,每次 send() 调用产生的输出也各不相同。这就是动态方法分派的作用!
亲手试试
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 读取输入
String recipient = scanner.nextLine();
String subject = scanner.nextLine();
String phoneNumber = scanner.nextLine();
// TODO:创建一个包含 3 个元素的 Notification 引用数组
// 数组应按以下顺序包含:
// 1. 一个基础 Notification 对象
// 2. 一个 EmailNotification 对象
// 3. 一个 SMSNotification 对象
// 它们都使用同一个 recipient
// TODO:遍历数组,对每个元素调用 send()
// 这演示了运行时多态!
}
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Java 在线编译器