什么是多态?
我们简单回顾一下多态 的概念,多态的字面意思是“多种形态”,在Java中,它意味着同一个接口,使用不同的实例而执行不同操作。

就是允许你将子类对象当作父类对象来使用,当调用同一个方法时,实际执行的是子类重写后的方法。
多态的实现需要满足三个必要条件:
- 继承:必须有类之间的继承关系。
- 重写:子类必须重写父类的方法。
- 向上转型:父类引用指向子类对象。
多态的实现机制
Java实现多态的核心机制可以归结为两点:
- 方法重写
- 动态绑定
下面我们详细拆解这两个机制是如何协同工作的。

方法重写
这是多态的基础,当子类觉得父类的某个方法不满足自己的需求时,可以提供一个具有相同方法名、相同参数列表、相同返回值类型(或其子类)的新实现,这个过程就是重写。
为什么重写是实现多态的基础? 因为重写保证了子类和父类拥有一个“共同的接口”(即相同的方法签名),这样,我们才能用同一个父类引用去调用不同子类的同名方法,而不会在编译时出错。
示例:
// 父类
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
// 子类
class Dog extends Animal {
@Override // 注解,确保方法是重写而不是重载
public void makeSound() {
System.out.println("Woof! Woof!");
}
}
// 另一个子类
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
Dog和Cat都重写了Animal的makeSound()方法,这为多态提供了可能性。
动态绑定
这是多态在运行时生效的关键,也称为晚期绑定或运行时绑定。
在Java中,当通过父类引用调用一个方法时,JVM(Java虚拟机)并不会在编译期就决定调用哪个版本的方法(这叫静态绑定),而是在运行期根据引用变量实际指向的对象类型来决定调用哪个类的方法。
这个过程大致如下:
- 编译阶段:编译器只关心引用变量的声明类型,它会检查
Animal类中是否有makeSound()方法,如果有,编译通过,编译器认为要调用的是Animal类的makeSound()方法。 - 运行阶段:JVM接管控制,它会查看引用变量实际指向的对象,如果这个对象是
Dog实例,JVM就会在Dog类中查找makeSound()方法并执行它,如果这个对象是Cat实例,JVM就会执行Cat类中的makeSound()方法。
示例代码与执行流程:
public class PolymorphismDemo {
public static void main(String[] args) {
// 1. 父类引用指向子类对象 (向上转型)
Animal myDog = new Dog();
Animal myCat = new Cat();
// 2. 通过父类引用调用方法
myDog.makeSound(); // 输出: Woof! Woof!
myCat.makeSound(); // 输出: Meow!
}
}
执行过程分析:
- 对于
myDog.makeSound():- 编译时:
myDog的类型是Animal,编译器检查Animal类,确认有makeSound()方法,代码合法。 - 运行时:JVM发现
myDog这个引用变量实际指向的是一个Dog对象,它去Dog类中查找makeSound()方法,并执行其代码,打印出 "Woof! Woof!"。
- 编译时:
- 对于
myCat.makeSound():- 编译时:同理,
myCat的类型是Animal,编译通过。 - 运行时:JVM发现
myCat实际指向的是Cat对象,于是执行Cat类的makeSound()方法,打印出 "Meow!"。
- 编译时:同理,
myDog.makeSound() 和 myCat.makeSound() 这两行代码在编译时看起来完全一样,但在运行时却根据对象的不同执行了不同的逻辑,这就是动态绑定的威力,也是多态的核心实现机制。
一个完整的例子
让我们用一个更贴近现实的例子来巩固理解。
// 父类
class Vehicle {
public void start() {
System.out.println("Vehicle is starting...");
}
}
// 子类1
class Car extends Vehicle {
@Override
public void start() {
System.out.println("Car is starting with a key turn.");
}
}
// 子类2
class ElectricScooter extends Vehicle {
@Override
public void start() {
System.out.println("Electric Scooter is starting with a button press.");
}
}
// 测试类
public class Test {
public static void main(String[] args) {
// 创建一个父类数组,存放不同的子类对象
Vehicle[] vehicles = new Vehicle[3];
vehicles[0] = new Car();
vehicles[1] = new ElectricScooter();
vehicles[2] = new Car(); // 可以重复存放同一个子类
// 使用多态统一调用
for (Vehicle v : vehicles) {
v.start();
}
}
}
输出结果:
Car is starting with a key turn.
Electric Scooter is starting with a button press.
Car is starting with a key turn.
分析:
- 我们定义了一个
Vehicle类型的数组vehicles。 - 我们将
Car和ElectricScooter的对象(通过向上转型)存入这个数组。 - 在
for循环中,我们使用Vehicle类型的引用v来调用start()方法。 - 编译时:编译器只知道
v是Vehicle类型,它会调用Vehicle的start()方法。 - 运行时:JVM根据
v在每次循环中实际指向的对象类型,动态地决定调用Car的start()还是ElectricScooter的start()。
这个例子完美地展示了多态的强大之处:我们可以用统一的方式(Vehicle引用)来处理不同类型的对象,让代码更加简洁、灵活和可扩展。
| 机制 | 作用 | 阶段 | 举例 |
|---|---|---|---|
| 方法重写 | 基础,提供相同方法签名但实现不同的版本,确保接口统一。 | 编译时/开发时 | Dog类重写Animal的makeSound()。 |
| 动态绑定 | 核心,在运行时,JVM根据对象实际类型(而非引用类型)来决定调用哪个方法。 | 运行时 | Animal myDog = new Dog(); myDog.makeSound(); 在运行时调用Dog的方法。 |
一句话总结Java多态的机制: 通过继承和重写建立方法的多版本,再通过向上转型和动态绑定在运行时根据实际对象类型来选择执行哪个版本的方法,从而实现“一种接口,多种行为”的效果。
