Java重写机制的本质是动态分派,JVM依据对象实际运行时类型(而非引用类型)通过invokevirtual指令查找并调用重写方法,接口或父类参数声明不影响该过程。

Java 重写机制在运行期的方法定位,本质就是动态分派——JVM 不看变量声明类型,只认对象真实类型。
动态分派的核心:invokevirtual 指令
所有非 final、非 static、非 private 的实例方法调用,字节码中都使用 invokevirtual 指令。它不直接指向具体实现,而是依赖运行时信息:
- JVM 查看操作数栈顶的对象引用,获取其**实际运行时类型(runtime type)**,比如
new B()就是类 B 的实例; - 从该类型开始,沿继承链向上搜索匹配的方法签名(名称 + 描述符);
- 一旦找到可访问的、已重写的方法,就返回其直接引用并执行;
- 若一直查到 Object 都没找到,抛出
AbstractMethodError(接口方法未实现)或IncompatibleClassChangeError(签名冲突)。
接口参数不影响动态分派结果
声明为接口类型(如 In i)或父类类型(如 A a)只是编译期约束,对运行期无影响:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
-
test1(In i) { i.method(); }和test2(A a) { a.method(); }中,只要传入的是new B(),两者都调用B.method(); - 因为 JVM 只检查
i或a所指向对象的**真实类信息**,而这个信息在堆内存中始终完整保留; - 接口本身不提供实现,但实现类(B)携带完整的 vtable(虚方法表),JVM 通过它快速定位重写版本。
什么情况下动态分派不生效?
不是所有方法调用都走动态分派。以下情况会绕过它,转为静态解析或特殊处理:
立即学习“Java免费学习笔记(深入)”;
-
static 方法:绑定到声明类型,
A.staticM()永远调用 A 的,子类同名方法是独立的; - private / final / 构造器:属于非虚方法,编译期就解析为直接引用,无法被重写,自然不参与动态分派;
-
字段访问:字段不具有多态性,
a.field总是取声明类型 A 中定义的 field,哪怕子类 B 有同名字段也视而不见; -
接口默认方法未被重写:若 B 没重写
In.method(),JVM 会回退到接口的 default 实现,仍属动态分派范畴,但目标是接口而非实现类。
为什么重载(Overload)不算动态分派?
重载方法的选择完全由编译器完成,依据是**所有参数的静态类型**,与运行时对象无关:
-
void m(A a)和void m(B b)是两个不同方法签名; -
A x = new B(); m(x);调用的是m(A a),因为 x 的静态类型是 A; - 即使运行时 x 指向 B 实例,编译器早已锁定方法版本,JVM 执行时不再重新判断。

















