跳转至

方法重载的底层原理?

结论

方法重载(Overload)在编译期就决定了调用哪个版本,靠的是编译器的静态分派(Static Dispatch),本质是方法签名的比较。JVM 字节码层面,重载方法调用通常用 invokevirtual / invokestatic,但"调哪个重载版本"在编译期已经写死在常量池的方法描述符里。

编译期如何确定重载版本

方法签名 = 方法名 + 参数列表(个数、类型、顺序)。返回值不算签名的一部分。

编译器在编译时做"最匹配版本"选择:

  1. 先找方法名相同、参数个数相同的候选。
  2. 类型继承链向上匹配,选择"最具体"的那个版本。
  3. 如果出现装箱、变长参数,按 JLS §15.12.2 的三阶段匹配:
  4. 第一阶段:不允许装箱/拆箱、不允许变长参数。
  5. 第二阶段:允许装箱/拆箱。
  6. 第三阶段:允许变长参数。

代码示例

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):泛型擦除后,编译器生成桥接方法来保证重写语义,这是重载与重写在泛型场景下的交叉点。