Menu
CoddyTech

方法重写(运行时)

本课属于 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();  // 每个对象都会调用自己的版本
}

运行时多态让你能够编写灵活的代码,使用父类型,同时自动采用正确的子类型行为。

challenge icon

挑战

简单

让我们构建一个通知系统,演示运行时多态如何发挥作用。你将创建一个通知类型的层次结构,在运行时,同一个方法调用会根据对象的实际类型产生不同的输出。

你将把代码分成 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()
        // 这演示了运行时多态!
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器