java 多态

如何理解java的多态和实现方式?
 

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 方法都不参与运行时多态,因为它们不是通过动态绑定进行调用。在实际项目中,多态广泛应用于支付、消息通知、策略模式、工厂模式等场景,可以显著提高系统的扩展性和维护性。

 
 
 
 
 
 
 
 
 

posted on 2026-07-21 08:54  日思日睿  阅读(3)  评论(0)    收藏  举报