引言:什么是多态?
多态(Polymorphism)是面向对象编程的三大特性之一(封装、继承、多态)。多态意味着"多种形态",即同一类型的引用指向不同类型的对象时,表现出不同的行为。
多态类比
想象一个遥控器(父类引用):
├── 按下"播放"键
│ ├── 电视(对象)→ 播放电视节目
│ ├── DVD机(对象)→ 播放DVD
│ └── 音响(对象)→ 播放音乐
│
├── 同样的"播放"动作
├── 不同的执行结果
└── 取决于具体是哪个设备
多态的好处:
- 提高代码复用性:父类引用可以指向不同子类对象
- 提高程序可扩展性:新增子类无需修改已有代码
- 使程序更灵活:同一接口,不同实现
向上转型(Upcasting)
概念
向上转型是将子类对象赋值给父类引用。这是自动转换,安全,因为子类包含了父类的所有特性。
语法
父类 父类引用 = new 子类();
示例
class Animal {
public void eat() {
System.out.println("动物在吃东西");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗在吃狗粮");
}
public void bark() {
System.out.println("汪汪汪");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫在吃猫粮");
}
public void meow() {
System.out.println("喵喵喵");
}
}
// 向上转型
Animal animal1 = new Dog(); // Dog对象赋值给Animal引用
Animal animal2 = new Cat(); // Cat对象赋值给Animal引用
animal1.eat(); // 输出:狗在吃狗粮(调用的是Dog的方法!)
animal2.eat(); // 输出:猫在吃猫粮(调用的是Cat的方法!)
执行流程图
创建子类对象
→
赋值给父类引用
→
调用方法
→
执行子类重写的方法
向下转型(Downcasting)
概念
向下转型是将父类引用转回子类类型。需要强制转换,且存在风险。
语法
子类 子类引用 = (子类) 父类引用;
示例
Animal animal = new Dog(); // 向上转型
animal.eat(); // 只能调用父类有的方法
// 向下转型:需要强制转换
Dog dog = (Dog) animal;
dog.bark(); // 现在可以调用Dog特有的方法了
危险:向下转型可能抛出ClassCastException。如果父类引用指向的不是目标子类,会转型失败。
instanceof运算符
概念
instanceof用于在向下转型前检查对象是否是某个类型的实例,避免ClassCastException。
语法
if (对象 instanceof 类型) {
// 安全,可以进行转型
}
完整示例
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();
} else {
System.out.println("animal不是Dog或Cat类型");
}
Java 14+的pattern matching for instanceof
Java 14引入了更简洁的语法:
// 传统写法
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
}
// Java 14+ 简化写法
if (animal instanceof Dog dog) {
dog.bark(); // 直接使用dog,无需强制转型
}
多态的应用场景
1. 方法参数多态
public class Vet {
public void giveShot(Animal animal) {
System.out.println("给动物打针...");
animal.eat(); // 同一个方法,不同对象不同行为
}
}
// 使用
Vet vet = new Vet();
vet.giveShot(new Dog()); // 给狗打针
vet.giveShot(new Cat()); // 给猫打针
2. 方法返回类型多态
public class AnimalFactory {
public Animal createAnimal(String type) {
if ("dog".equals(type)) {
return new Dog();
} else if ("cat".equals(type)) {
return new Cat();
} else {
return new Animal();
}
}
}
// 使用
AnimalFactory factory = new AnimalFactory();
Animal animal1 = factory.createAnimal("dog");
Animal animal2 = factory.createAnimal("cat");
3. 多态数组
Animal[] animals = new Animal[3];
animals[0] = new Dog();
animals[1] = new Cat();
animals[2] = new Dog();
for (Animal animal : animals) {
animal.eat(); // 同一个方法调用,不同对象不同行为
}
4. 集合的多态
List animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
for (Animal animal : animals) {
animal.eat();
}
多态的原理
静态绑定 vs 动态绑定
| 类型 | 绑定时机 | 方法类型 | 示例 |
|---|---|---|---|
| 静态绑定 | 编译时 | private、static、final方法 | 方法重载 |
| 动态绑定 | 运行时 | 实例方法(可被重写) | 方法重写、多态 |
方法调用原理
Animal animal = new Dog();
animal.eat();
// JVM执行过程:
// 1. 查看animal引用的实际类型是Dog
// 2. 在Dog类中查找eat()方法
// 3. 如果Dog重写了eat(),调用Dog的eat()
// 4. 如果Dog没有重写,调用父类Animal的eat()
多态与重写的区别
| 特性 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 发生位置 | 父子类之间 | 同一类中 |
| 方法名 | 相同 | 相同 |
| 参数列表 | 相同 | 不同 |
| 返回类型 | 相同或子类型 | 可以不同 |
| 多态表现 | 是 | 否(编译时多态) |
实战练习
练习:支付系统
interface Payment {
void pay(double amount);
}
class Alipay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付:" + amount + "元");
}
}
class WechatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用微信支付:" + amount + "元");
}
}
class CreditCard implements Payment {
@Override
public void pay(double amount) {
System.out.println("使用信用卡支付:" + amount + "元");
}
}
// 多态使用
Payment payment = new Alipay();
payment.pay(100); // 使用支付宝
payment = new WechatPay();
payment.pay(200); // 使用微信支付
小结
- 多态是同一类型引用指向不同类型对象,表现出不同行为
- 向上转型:子类转父类,自动进行,安全
- 向下转型:父类转子类,需强制转换,可能抛出ClassCastException
- instanceof:用于在向下转型前检查类型
- 多态应用:参数多态、返回值多态、多态数组、集合多态
- 动态绑定:运行时根据实际对象类型决定调用哪个方法
☕ Java 在线代码编辑器
📝 运行结果