Java 中的多态(Polymorphism),是面向对象三大特性(封装、继承、多态)之一,也是面试中最常考的知识点。
很多人认为多态就是“父类引用指向子类对象”,其实这只是多态的表现形式,不是它的本质。
多态的本质:同一个接口(方法调用),根据对象的不同,表现出不同的行为。
例如:
public class Animal {
public void eat() {
System.out.println("动物吃东西");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
调用:
Animal animal = new Dog();
animal.eat();
输出
狗吃骨头
如果换成
Animal animal = new Cat();
animal.eat();
输出
猫吃鱼
可以看到:
同样都是
animal.eat();
由于实际对象不同,执行的代码也不同。
这就是多态。
多态成立的三个条件
Java 中实现运行时多态,必须满足三个条件。
① 有继承(Inheritance)
class Animal {}
class Dog extends Animal {}
没有继承,就不存在多态。
② 有方法重写(Override)
父类
public void eat() {
}
子类
@Override
public void eat() {
}
如果没有重写:
class Animal{
public void eat(){
System.out.println("Animal");
}
}
class Dog extends Animal{
}
调用
Animal animal = new Dog();
animal.eat();
输出仍然是
Animal
因为 Dog 没有自己的实现。
③ 父类引用指向子类对象
Animal animal = new Dog();
而不是
Dog dog = new Dog();
只有
Animal animal = new Dog();
Java 才需要运行时去判断到底执行哪个方法。
Java 多态是如何实现的?
很多人只停留在
父类引用指向子类对象
真正面试官更喜欢问:
JVM 是如何实现多态的?
答案就是:
动态绑定(Dynamic Dispatch)+ 虚方法表(Virtual Method Table,vtable)
第一步:编译阶段
例如:
Animal animal = new Dog();
animal.eat();
编译器只知道:
animal 的类型是 Animal
因此生成的字节码:
invokevirtual Animal.eat()
注意:
编译器不知道最终对象是不是 Dog。
第二步:运行阶段
真正执行时
JVM 会发现:
animal
↓
实际上指向 Dog 对象
于是:
Dog.eat()
而不是
Animal.eat()
这就是
动态绑定(Dynamic Binding)
即:
方法到底调用哪个版本,不是在编译时决定,而是在运行时决定。
JVM 是如何找到 Dog.eat() 的?
JVM 内部,每个类都有一个
虚方法表(Virtual Method Table)
例如:
Animal
Animal VTable
eat() -----> Animal.eat()
sleep() ---> Animal.sleep()
run() -----> Animal.run()
Dog
Dog VTable
eat() -----> Dog.eat()
sleep() ---> Animal.sleep()
run() -----> Animal.run()
Dog 重写了 eat
所以
eat()
↓
Dog.eat()
没有重写的方法
sleep()
↓
Animal.sleep()
调用
animal.eat();
实际上流程就是:
animal
↓
找到对象
↓
找到对象所属Class
↓
找到VTable
↓
查eat()
↓
Dog.eat()
↓
执行
所以:
Java 多态,本质就是 JVM 在运行期间根据对象实际类型查找虚方法表并完成动态绑定。
为什么成员变量没有多态?
例如:
class Animal{
String name = "Animal";
}
class Dog extends Animal{
String name = "Dog";
}
Animal animal = new Dog();
System.out.println(animal.name);
输出
Animal
很多新人都会觉得应该输出 Dog。
实际上:
成员变量没有重写,只有隐藏(Hide)。
变量访问:
animal.name
编译器已经知道:
animal
↓
Animal
因此直接访问
Animal.name
变量访问属于
静态绑定(Static Binding)
不会发生动态绑定。
为什么 static 方法没有多态?
class Animal{
static void eat(){
System.out.println("Animal");
}
}
class Dog extends Animal{
static void eat(){
System.out.println("Dog");
}
}
调用:
Animal animal = new Dog();
animal.eat();
输出
Animal
原因:
static 属于类。
调用的是:
Animal.eat()
不是对象。
因此:
静态方法不能重写
只能隐藏。
为什么 final 方法没有多态?
class Animal{
final void eat(){
}
}
子类
不能Override
既然不能 Override,
自然不存在动态绑定。
private 方法为什么没有多态?
private 方法:
子类不可见
不能 Override。
因此
private 方法
↓
编译时就确定
↓
invokeSpecial
不是 invokevirtual。
Java 中多态有哪些实现方式?
Java 中的多态主要有以下几种:
实现方式是否运行时多态原理
方法重写(Override)
✅ 是
动态绑定(Dynamic Dispatch)
接口实现(Interface)
✅ 是
不同实现类执行不同逻辑
抽象类
✅ 是
子类实现抽象方法
方法重载(Overload)
❌ 否
编译期多态(静态多态)
泛型
❌ 否
参数类型约束,不属于多态机制
例如接口:
interface Payment{
void pay();
}
class AliPay implements Payment{
public void pay(){
System.out.println("支付宝");
}
}
class WechatPay implements Payment{
public void pay(){
System.out.println("微信");
}
}
Payment payment = new AliPay();
payment.pay();
payment = new WechatPay();
payment.pay();
调用代码完全一样:
payment.pay();
但实际行为不同,这也是典型的运行时多态。
生产中的经典应用
以支付系统为例,不同支付渠道都实现统一接口:
public interface PayService {
void pay(Order order);
}
public class AlipayService implements PayService {
@Override
public void pay(Order order) {
// 支付宝支付
}
}
public class WechatPayService implements PayService {
@Override
public void pay(Order order) {
// 微信支付
}
}
public class UnionPayService implements PayService {
@Override
public void pay(Order order) {
// 银联支付
}
}
业务层无需关心具体实现:
PayService payService = getPayService(channel);
payService.pay(order);
新增一种支付方式(如 PayPal)时,只需新增一个实现类,而无需修改原有业务逻辑。这体现了多态带来的可扩展性,也符合开闭原则(对扩展开放,对修改关闭)。
面试回答模板(2 分钟)
Java 的多态指的是同一个方法调用,在运行时根据对象的实际类型表现出不同的行为。实现运行时多态需要满足三个条件:继承、方法重写、父类(或接口)引用指向子类对象。编译阶段,编译器只知道引用类型,会生成 invokevirtual 等调用指令;运行阶段,JVM 根据对象的实际类型,通过动态绑定查找对应类的虚方法表(vtable),最终调用真正的方法实现。需要注意的是,成员变量、static 方法、final 方法和 private 方法都不参与运行时多态,因为它们不是通过动态绑定进行调用。在实际项目中,多态广泛应用于支付、消息通知、策略模式、工厂模式等场景,可以显著提高系统的扩展性和维护性。