教程中心 / Java教程 / 多态

多态

18分钟 面向对象

引言:什么是多态?

多态(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 在线代码编辑器
📝 运行结果