方法重载的底层原理?¶
结论¶
方法重载(Overload)在编译期就决定了调用哪个版本,靠的是编译器的静态分派(Static Dispatch),本质是方法签名的比较。JVM 字节码层面,重载方法调用通常用 invokevirtual / invokestatic,但"调哪个重载版本"在编译期已经写死在常量池的方法描述符里。
编译期如何确定重载版本¶
方法签名 = 方法名 + 参数列表(个数、类型、顺序)。返回值不算签名的一部分。
编译器在编译时做"最匹配版本"选择:
- 先找方法名相同、参数个数相同的候选。
- 按类型继承链向上匹配,选择"最具体"的那个版本。
- 如果出现装箱、变长参数,按 JLS §15.12.2 的三阶段匹配:
- 第一阶段:不允许装箱/拆箱、不允许变长参数。
- 第二阶段:允许装箱/拆箱。
- 第三阶段:允许变长参数。
代码示例¶
public class OverloadDemo {
public void print(Object o) { System.out.println("Object"); }
public void print(String s) { System.out.println("String"); }
public void print(Integer i) { System.out.println("Integer"); }
public static void main(String[] args) {
OverloadDemo d = new OverloadDemo();
d.print(null); // 编译通过:String 比 Object 更具体
d.print(1); // Integer(先装箱,不是 int -> long -> Object)
}
}
d.print(null) 会调用 print(String),因为 String 在继承树上比 Object 更"近"。
字节码视角¶
public static void main(java.lang.String[]);
Code:
aload_0
aconst_null
invokevirtual OverloadDemo.print(Ljava/lang/String;)V // 描述符已写死
注意描述符 (Ljava/lang/String;)V,JVM 运行时不需要再判断类型。
易混点:重载 vs 重写
- 重载(Overload):编译期,静态分派,看引用类型与参数。
- 重写(Override):运行期,动态分派,看对象实际类型,通过
invokevirtual+ 虚方法表(vtable)实现。
高频追问
- 为什么返回值不能区分重载?因为调用者可能忽略返回值,编译器无法唯一确定版本。
- 桥接方法(Bridge Method):泛型擦除后,编译器生成桥接方法来保证重写语义,这是重载与重写在泛型场景下的交叉点。