java方法重写的底层机制是jvm通过invokevirtual指令触发动态绑定,依赖对象实际类型查找虚方法表(vtable)中对应槽位的方法入口,实现运行时多态;该过程在类加载验证阶段严格校验签名、访问修饰符、异常及返回类型协变等规则,确保重写安全有效。

Java 中方法重写(Override)的底层机制,核心在于运行时多态和虚方法调用(virtual method invocation),由 JVM 的 invokevirtual 指令驱动,依赖类的运行时类型信息与虚方法表(vtable)协同工作。
invokevirtual 指令触发动态绑定
编译期 javac 只做静态检查(如访问权限、签名匹配),生成的字节码中对非 private/static/final 方法的调用统一使用 invokevirtual 指令。该指令不硬编码目标方法地址,而是根据对象实际类型在运行时查找并跳转到对应实现。
- JVM 执行 invokevirtual 时,先从操作数栈顶获取对象引用,读取其实际 class 对象(即堆中对象的 klass 指针)
- 再通过该 class 查找其虚方法表(vtable),定位方法在表中的槽位(slot)
- 最终跳转到 vtable 中该槽位指向的具体字节码入口(可能是子类重写后的方法)
虚方法表(vtable)的结构与填充时机
vtable 是每个类(除接口外)在类加载的准备阶段之后、初始化之前由 JVM 构建的函数指针数组,每个槽位对应一个可被重写的方法(public/protected/default 且非 static/private/final)。
- 父类 vtable 中的方法槽,在子类 vtable 中被继承;若子类重写了某方法,该槽会被替换为子类版本的入口地址
- 新增的重写方法直接填入父类 vtable 已有槽位;新增的非重写方法则追加到 vtable 末尾(不同 JVM 实现有差异,HotSpot 使用“继承+扩展”方式)
- final 方法不进入 vtable(编译期就内联或直接调用 invokespecial),private 和 static 方法也不参与虚分派
方法签名匹配与重写规则的运行时保障
重写是否生效,不仅取决于源码语法,更由 JVM 在类加载阶段严格校验:
- 子类方法必须与父类方法具有相同名称、相同参数类型列表(包括泛型擦除后一致),返回类型协变(如父类返回 Object,子类可返回 String)
- 访问修饰符不能更严格(如父类 protected,子类不能是 private),但可更宽松(如 default → public)
- 异常声明不能新增受检异常(Checked Exception),仅可减少或不变
- 这些规则在类验证阶段(Verification)被检查,违反则抛出 VerifyError,不会等到运行时才失败
与重载(Overload)的本质区别
重载是编译期行为,由 javac 根据静态类型 + 参数字面量类型决定调用哪个方法,生成 invokespecial 或 invokestatic 等指令;而重写是运行期行为,依赖对象真实类型查 vtable。
- 同一个类中多个同名但参数不同的方法,各自独立存在于常量池,无 vtable 关系
- 重载方法的选择在字节码中已固化;重写方法的目标只有运行时才能确定
- 这也解释了为何“重载看左边(引用类型),重写看右边(实例类型)”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











