Java多态方法调用核心靠运行时动态绑定与方法表查找,依据实际对象类型(如Bulldog)直接查其类方法表,若重写则执行本类实现,否则条目已指向父类实现,无需逐层回溯。

Java 中多态在多层继承结构中确定方法调用目标,核心靠 运行时动态绑定 + 方法表(vtable)查找,不是看引用类型,而是看实际对象所属的最具体子类,再结合该类是否重写了目标方法。
多层继承下方法调用的判定逻辑
假设存在四层继承链:Animal → Mammal → Dog → Bulldog
其中 sound() 方法在 Animal 中定义,在 Dog 和 Bulldog 中都被重写。
当你写:
Animal a = new Bulldog(); a.sound();
JVM 不会按 Animal → Mammal → Dog → Bulldog 逐层往上或往下搜索,而是直接查 Bulldog 类的方法表:
- 若
Bulldog重写了sound()→ 调用Bulldog.sound() - 若
Bulldog没重写,但Dog重写了 → 调用Dog.sound()(因为Bulldog的方法表里sound条目指向Dog的实现) - 若
Dog也没重写,而Mammal或Animal是唯一定义处 → 调用Animal.sound()
关键点:方法表继承并可覆盖。每个类的方法表都包含从父类继承来的方法条目,只要子类重写,该条目就指向子类自己的字节码地址。
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
决定调用哪个版本的三个关键事实
- 方法必须是非 static、非 private、非 final 的实例方法(只有这些才参与动态绑定)
- 调用发生时,JVM 根据对象的实际运行时类型(这里是
Bulldog),定位其类的方法表 - 在该类的方法表中查找目标方法名和签名匹配的条目;若当前类没重写,条目仍指向父类实现(无需跳转,表里已存正确地址)
例如:
class Animal { void sound() { System.out.println("animal"); } }
class Mammal extends Animal { } // 未重写
class Dog extends Mammal { void sound() { System.out.println("woof"); } }
class Bulldog extends Dog { void sound() { System.out.println("grrr"); } }new Bulldog().sound() → 查 Bulldog 表 → 有重写 → 执行 "grrr"new Dog().sound() → 查 Dog 表 → 有重写 → 执行 "woof"new Mammal().sound() → 查 Mammal 表 → 无重写 → 条目指向 Animal.sound() → 执行 "animal"
特殊情况:构造器中调用重写方法要小心
在构造过程中,子类对象尚未完全初始化,但父类构造器可能已调用被子类重写的方法:
class Animal {
Animal() { sound(); } // 实际调用的是子类重写的 version
void sound() { System.out.println("animal"); }
}
class Dog extends Animal {
private String name = "Leo";
void sound() { System.out.println(name); } // 此时 name 还未赋值!输出 null
}这是因为:方法调用始终按实际对象类型走动态绑定,哪怕此时子类字段还没初始化完毕。
总结判断步骤(实际运行时)
- 看
new后面的类 —— 它决定了对象的真实类型,也就是方法表的起点 - 查该类的方法表中对应方法的入口地址
- 地址指向哪里,就执行哪里的代码;这个地址在类加载时就填好了,由重写关系决定
- 不存在“回溯父类找实现”的过程,方法表已固化继承与覆盖关系
不复杂但容易忽略

















