第 18 章:多态
学习目标
- 理解多态的本质:动态绑定
- 掌握向上转型和向下转型
- 学会 instanceof 和模式匹配
- 理解多态的应用场景
一、什么是多态?
多态 = 同一个方法调用,在不同对象上有不同行为。
java
Animal animal1 = new Dog(); // ① 父类引用指向子类对象
Animal animal2 = new Cat();
animal1.makeSound(); // 汪汪汪
animal2.makeSound(); // 喵喵喵
// 同一个调用 makeSound(),表现不同 → 多态二、多态的三要素
- 继承:子类继承父类
- 重写:子类重写父类方法
- 父类引用指向子类对象:向上转型
java
class Animal {
public void sound() {
System.out.println("动物叫");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("汪汪汪");
}
}
// ✅ 多态:父类引用指向子类对象
Animal a = new Dog();
a.sound(); // 汪汪汪(运行时绑定到 Dog 的实现)三、向上转型
向上转型 = 子类对象赋给父类引用(自动转换,安全)。
java
Dog dog = new Dog();
Animal animal = dog; // ① 自动向上转型
// animal 实际上还是 Dog 对象,只是被当作 Animal 看向上转型的限制
java
Animal animal = new Dog();
animal.sound(); // ✅ 父类有的方法可以调用
// animal.bark(); // ❌ 编译报错:父类没有 bark 方法向上转型后只能调用父类声明的方法,不能调用子类特有方法。
四、向下转型
向下转型 = 父类引用转回子类引用(需要强制转换,可能失败)。
java
Animal animal = new Dog();
// 必须先判断类型
if (animal instanceof Dog) { // ① instanceof 类型检查
Dog dog = (Dog) animal; // ② 强制向下转型
dog.bark(); // ③ 现在可以调用子类方法
}ClassCastException
java
Animal animal = new Cat();
Dog dog = (Dog) animal; // ❌ 运行时 ClassCastException⚠️ 向下转型必须用 instanceof 检查,否则可能运行崩溃。
模式匹配(JDK 16+)
java
// 传统写法
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
// ✅ 模式匹配:自动绑定变量
if (animal instanceof Dog dog) { // ① 自动转型
dog.bark(); // ② 直接用
}五、动态绑定机制
多态的核心:方法调用在运行时才决定调用哪个版本。
java
class Animal {
public void sound() { System.out.println("A"); }
}
class Dog extends Animal {
@Override
public void sound() { System.out.println("D"); }
}
Animal a = new Dog();
a.sound(); // 输出 D(运行时看实际类型)编译看左边,运行看右边 —— 多态的铁律。
六、多态的实际应用
6.1 统一参数
java
public class AnimalService {
public void printSound(Animal animal) { // ① 接受所有 Animal 子类
animal.sound();
}
}
AnimalService service = new AnimalService();
service.printSound(new Dog()); // 汪汪汪
service.printSound(new Cat()); // 喵喵喵6.2 集合多态
java
Animal[] animals = { new Dog(), new Cat(), new Bird() };
for (Animal a : animals) {
a.sound(); // 各自调用自己的实现
}6.3 模板方法模式
java
abstract class DataProcessor {
public final void process() { // ① 模板方法(固定流程)
load();
parse(); // ② 钩子方法(子类实现)
save();
}
protected abstract void load();
protected abstract void parse();
protected abstract void save();
}
class CsvProcessor extends DataProcessor {
@Override protected void load() { /* 加载 CSV */ }
@Override protected void parse() { /* 解析 CSV */ }
@Override protected void save() { /* 保存 */ }
}七、多态的局限
7.1 字段没有多态
java
class Parent {
public int x = 1;
}
class Child extends Parent {
public int x = 2;
}
Parent p = new Child();
System.out.println(p.x); // 输出 1(字段不参与多态)
System.out.println(((Child) p).x); // 输出 2字段没有多态,方法才有。
7.2 static 方法没有多态
java
class Parent {
public static void hello() { System.out.println("P"); }
}
class Child extends Parent {
public static void hello() { System.out.println("C"); }
}
Parent p = new Child();
p.hello(); // 输出 P(static 方法不参与多态)7.3 private 方法不可重写
java
class Parent {
private void hello() { System.out.println("P"); }
}
class Child extends Parent {
public void hello() { System.out.println("C"); } // 不是重写,是新方法
}八、完整代码示例
java
/**
* 多态综合示例
*
* 配套文档:第 18 章 多态
*/
// ============ 父类 ============
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println(name + " 发出声音");
}
public void move() {
System.out.println(name + " 在移动");
}
}
// ============ 子类 ============
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " 汪汪汪");
}
public void fetch() {
System.out.println(name + " 在捡球");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + " 喵喵喵");
}
public void climb() {
System.out.println(name + " 在爬树");
}
}
// ============ 测试 ============
public class PolymorphismDemo {
public static void main(String[] args) {
// ============ 1. 多态:父类引用指向子类对象 ============
System.out.println("=== 多态演示 ===");
Animal a1 = new Dog("旺财");
Animal a2 = new Cat("小花");
a1.makeSound(); // 旺财 汪汪汪
a2.makeSound(); // 小花 喵喵喵
// ============ 2. 向上转型的限制 ============
System.out.println("\n=== 向上转型 ===");
// a1.fetch(); // ❌ 编译报错:Animal 没有 fetch
// a2.climb(); // ❌ 编译报错:Animal 没有 climb
// ============ 3. 向下转型 ============
System.out.println("\n=== 向下转型 ===");
if (a1 instanceof Dog) {
Dog dog = (Dog) a1;
dog.fetch(); // 旺财 在捡球
}
// ============ 4. 模式匹配(JDK 16+)============
if (a2 instanceof Cat cat) {
cat.climb(); // 小花 在爬树
}
// ============ 5. 多态数组 ============
System.out.println("\n=== 多态数组 ===");
Animal[] animals = { new Dog("A"), new Cat("B"), new Dog("C") };
for (Animal animal : animals) {
animal.makeSound(); // 各自的行为
}
}
}输出:
=== 多态演示 ===
旺财 汪汪汪
小花 喵喵喵
=== 向下转型 ===
旺财 在捡球
=== 模式匹配 ===
小花 在爬树
=== 多态数组 ===
A 汪汪汪
B 喵喵喵
C 汪汪汪九、本章小结
| 要点 | 关键 |
|---|---|
| 多态本质 | 同一个调用,不同对象有不同行为 |
| 三要素 | 继承 + 重写 + 父类引用指向子类 |
| 向上转型 | 子类 → 父类,自动安全 |
| 向下转型 | 父类 → 子类,强制,可能 ClassCastException |
| 编译看左 | 编译时看引用类型 |
| 运行看右 | 运行时看实际类型 |
| 字段没多态 | 字段不参与多态 |
| static 没多态 | static 方法不参与多态 |
动手练习
练习 1:基础题
定义一个 Shape 抽象类(有 area() 方法),创建 Circle 和 Rectangle 子类,演示多态。
练习 2:进阶题
实现一个 Employee 类和 Manager 子类,Manager 重写 work() 方法。写一个 Company 类用多态统一管理员工。