Appearance
第8课:多态
🎯 学习目标
- 理解多态的概念
- 掌握向上转型和向下转型
- 理解动态绑定
- 掌握 instanceof 运算符
📖 一、多态的概念
什么是多态?
多态(Polymorphism):同一个引用类型,指向不同的对象时,有不同的行为。
java
Animal animal1 = new Dog();
animal1.makeSound(); // Woof!
Animal animal2 = new Cat();
animal2.makeSound(); // Meow!
// 同样是 Animal 类型,但行为不同多态的三个必要条件
- ✅ 继承(或实现接口)
- ✅ 方法重写
- ✅ 父类引用指向子类对象
📖 二、向上转型
1. 基本概念
向上转型(Upcasting):子类对象赋值给父类引用,自动完成。
java
class Animal {
public void eat() {
System.out.println("Animal eating");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eating");
}
public void bark() {
System.out.println("Woof!");
}
}
// 向上转型(自动)
Animal animal = new Dog(); // Dog → Animal
animal.eat(); // Dog eating(调用 Dog 的方法)
// animal.bark(); // ❌ 编译错误(Animal 没有 bark 方法)2. 向上转型的特点
java
Animal animal = new Dog();
// ✅ 可以调用父类方法(重写后是子类实现)
animal.eat();
// ❌ 不能调用子类特有的方法
// animal.bark(); // 编译错误
// 可以访问父类的属性
System.out.println(animal.getClass()); // class Dog📖 三、向下转型
1. 基本概念
向下转型(Downcasting):父类引用转为子类引用,需要强制转换。
java
Animal animal = new Dog(); // 向上转型
// 向下转型(强制)
Dog dog = (Dog) animal;
dog.bark(); // Woof!(现在可以调用 Dog 的方法了)2. 向下转型的风险
java
Animal animal = new Cat();
// ❌ 运行时错误:ClassCastException
Dog dog = (Dog) animal; // Cat 不能转为 Dog3. instanceof 运算符
java
Animal animal = new Dog();
// 安全的向下转型
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.meow();
}
// Java 14+ 模式匹配
if (animal instanceof Dog dog) {
dog.bark(); // 自动转型
}📖 四、动态绑定
1. 静态绑定 vs 动态绑定
静态绑定:编译时确定(private、static、final 方法)
动态绑定:运行时确定(普通实例方法)
java
class Animal {
public void method() {
System.out.println("Animal");
}
}
class Dog extends Animal {
@Override
public void method() {
System.out.println("Dog");
}
}
// 动态绑定
Animal animal = new Dog();
animal.method(); // Dog(运行时决定)2. 属性不是多态的
java
class Animal {
String name = "Animal";
}
class Dog extends Animal {
String name = "Dog";
}
Animal animal = new Dog();
System.out.println(animal.name); // Animal(属性看引用类型)
Dog dog = (Dog) animal;
System.out.println(dog.name); // Dog📖 五、多态的应用
1. 参数多态
java
public class AnimalTest {
// 一个方法处理所有动物
public static void feedAnimal(Animal animal) {
animal.eat();
}
public static void main(String[] args) {
feedAnimal(new Dog()); // Dog eating
feedAnimal(new Cat()); // Cat eating
feedAnimal(new Bird()); // Bird eating
}
}2. 数组多态
java
Animal[] animals = {
new Dog(),
new Cat(),
new Bird()
};
for (Animal animal : animals) {
animal.eat();
// 调用子类特有方法
if (animal instanceof Dog) {
((Dog) animal).bark();
} else if (animal instanceof Cat) {
((Cat) animal).meow();
}
}3. 返回值多态
java
public class AnimalFactory {
public static Animal createAnimal(String type) {
switch (type) {
case "dog":
return new Dog();
case "cat":
return new Cat();
default:
return new Animal();
}
}
}
// 使用
Animal animal = AnimalFactory.createAnimal("dog");
animal.eat();📖 六、多态的优点
1. 提高代码的扩展性
java
// 不用多态
public void process(Dog dog) { dog.eat(); }
public void process(Cat cat) { cat.eat(); }
public void process(Bird bird) { bird.eat(); }
// 使用多态
public void process(Animal animal) {
animal.eat(); // 所有动物通用
}2. 简化代码
java
// 不用多态
List<Dog> dogs = new ArrayList<>();
List<Cat> cats = new ArrayList<>();
// ...
// 使用多态
List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());3. 降低耦合度
java
// 面向接口编程
public void saveData(Database db) {
db.save();
}
// 可以传入任何 Database 的实现
saveData(new MySQL());
saveData(new Oracle());
saveData(new MongoDB());💡 最佳实践
1. 面向接口编程
java
// ✅ 好:使用接口
List<String> list = new ArrayList<>();
// ❌ 不好:使用具体类
ArrayList<String> list = new ArrayList<>();2. 避免过度使用向下转型
java
// ❌ 不好:频繁向下转型
Animal animal = getAnimal();
if (animal instanceof Dog) {
((Dog) animal).bark();
} else if (animal instanceof Cat) {
((Cat) animal).meow();
}
// ✅ 好:在父类中定义统一接口
abstract class Animal {
public abstract void makeSound();
}3. 利用多态消除 if-else
java
// ❌ 不好
if (type.equals("dog")) {
dog.eat();
} else if (type.equals("cat")) {
cat.eat();
}
// ✅ 好
Animal animal = AnimalFactory.create(type);
animal.eat();⚠️ 常见陷阱
陷阱1:以为字段也会多态
Java 的字段访问看引用类型,方法调用才有动态绑定。不要用同名字段表达多态行为。
陷阱2:频繁向下转型
如果代码里到处都是 instanceof 和强制转换,通常说明父类或接口抽象不完整。
陷阱3:重载和重写混淆
重载在编译期根据参数类型选择,重写在运行期根据对象实际类型选择。
陷阱4:父类构造器中调用可重写方法
构造过程中子类字段可能还没初始化,父类构造器调用被子类重写的方法容易出现半初始化问题。
🆚 Java vs C 对比
| 特性 | C | Java 多态 |
|---|---|---|
| 动态行为 | 函数指针表手写 | 虚方法动态绑定 |
| 类型检查 | 手动约定 | 编译期类型系统 |
| 向下转型 | 指针强转,风险大 | 强转 + instanceof 检查 |
| 接口编程 | struct + 函数指针 | interface / abstract class |
对 C 程序员来说,多态类似“结构体里放函数指针实现不同策略”,但 Java 把这套机制内建到类、接口和虚方法调用中。
🧪 实战案例:支付方式
不用多态的写法:
java
if (type.equals("wechat")) {
payByWechat(amount);
} else if (type.equals("card")) {
payByCard(amount);
}使用多态:
java
interface Payment {
void pay(BigDecimal amount);
}
class WechatPayment implements Payment {
public void pay(BigDecimal amount) {
System.out.println("wechat pay " + amount);
}
}
class CardPayment implements Payment {
public void pay(BigDecimal amount) {
System.out.println("card pay " + amount);
}
}调用方只依赖接口:
java
public void checkout(Payment payment, BigDecimal amount) {
payment.pay(amount);
}新增支付方式时,新增实现类即可,调用方不需要知道具体类型。
✅ 掌握标准
学完本课后,应能做到:
text
能说出多态的三个必要条件。
能解释向上转型和向下转型。
能安全使用 instanceof。
能说明方法动态绑定和字段静态绑定的差异。
能区分重载和重写。
能用接口或父类参数接收不同实现。
能用多态减少 if-else 分支。
能识别过度向下转型带来的设计问题。多态的价值是让调用方依赖抽象,而不是依赖一堆具体类型判断。
📝 练习
完成 练习/Ex08_Polymorphism.java:
- 向上转型和向下转型
- instanceof 使用
- 动态绑定测试
- 参数多态
- 数组多态
- 综合:图形绘制系统
🎓 下一步
- 第9课:抽象类与接口 - abstract、interface、区别与选择