重载是编译期静态绑定,字节码中为多个签名不同的独立方法,调用使用invokestatic或invokevirtual直接指向唯一Methodref;重写是运行期动态绑定,父子类方法签名相同,调用统一用invokevirtual指向父类符号引用,JVM运行时查vtable跳转。

重载和重写在字节码层面的差异,核心在于绑定时机不同:重载是编译期静态绑定,重写是运行期动态绑定。这意味着它们在字节码中体现为完全不同的指令和结构,不是“看起来像”,而是“根本不同”。
重载:编译期决定,字节码里是多个独立方法
编译器根据调用处的实参类型和数量,直接选中一个具体方法,并在字节码中生成明确的方法引用(Methodref)。所有重载版本都会保留在类文件中,彼此无关。
- 每个重载方法在字节码中都有独立的
Method结构,签名不同(如test(I)V和test(Ljava/lang/String;)V) - 调用点使用
invokestatic或invokevirtual,操作数指向唯一确定的方法符号引用 - 没有“选择逻辑”——编译完就定死了,运行时不会切换
- 反编译后能看到多个同名但 descriptor 不同的方法,比如:
void test(int)
void test(String)
void test(int, String)
重写:运行期查表,字节码里靠 invokevirtual + 方法表跳转
重写不改变字节码中的调用指令形式,而是依赖 JVM 在运行时根据对象实际类型,查子类的方法区(vtable 或 itable)找到真正要执行的版本。
- 父类和子类中重写的方法,在字节码里 signature 完全一致(如都是
run()V) - 调用点统一用
invokevirtual指令,但操作数只指向父类方法的符号引用 - JVM 执行时,先获取栈顶对象的实际 class,再查该 class 的虚方法表,定位到子类实现的入口地址
- 反编译后看到的是“相同方法名+相同签名”,但子类 class 文件里有对应方法体,且父类引用调用时行为由实际类型决定
怎么验证?用 javap 看真实字节码
写个简单例子,编译后用 javap -v 查看:
立即学习“Java免费学习笔记(深入)”;
- 对重载:关注
Constant pool中多个Methodref条目,以及Code属性里每条invoke*指令的操作数索引——它指向哪个方法,一目了然 - 对重写:对比父类和子类的
methods区域,发现签名相同;再看调用方字节码,invokevirtual后面写的仍是父类方法名,但运行结果却是子类逻辑 - 关键线索:
invokestatic和invokespecial永远不会用于重写;只有invokevirtual(实例方法)和invokeinterface(接口默认/重写)才支持动态分派


















