
java 通过动态分派(dynamic dispatch)在运行时根据实际对象类型而非引用类型确定方法调用目标,接口变量虽无具体实现,但其指向的对象始终携带自身类信息,从而精准定位到重写后的方法。
java 通过动态分派(dynamic dispatch)在运行时根据实际对象类型而非引用类型确定方法调用目标,接口变量虽无具体实现,但其指向的对象始终携带自身类信息,从而精准定位到重写后的方法。
在 Java 中,方法调用的绑定时机分为两类:静态分派(Static Dispatch) 和 动态分派(Dynamic Dispatch)。理解二者差异,是掌握多态行为的关键。
静态分派:编译期决定,基于引用类型
静态分派发生在编译阶段,编译器仅依据变量声明的类型(即引用类型)来选择重载(overload)方法。它不关心实际对象是什么,只看“看起来像什么”。例如:
public class Dispatcher {
public void invoke(A a) { System.out.println("A version"); }
public void invoke(B b) { System.out.println("B version"); }
}
Dispatcher d = new Dispatcher();
d.invoke(new B()); // 输出 "B version" —— 编译器根据参数类型 B 选择方法注意:静态分派与 interface 或 extends 无关,它只作用于方法重载场景,且完全由编译器完成。
动态分派:运行期决定,基于实际对象类型
动态分派才是 Java 实现多态(尤其是重写/override)的核心机制。它发生在方法调用执行时,JVM 查看栈帧中接收者对象的实际运行时类型(runtime type),再按类继承链自底向上查找匹配的方法实现。
立即学习“Java免费学习笔记(深入)”;
回到原始示例:
public interface In { void method(); }
public class A implements In { public void method() { System.out.println("A"); } }
public class B extends A { public void method() { System.out.println("B"); } }
public void test1(In i) { i.method(); } // 动态分派:i 指向 B 实例 → 调用 B.method()
public void test2(A a) { a.method(); } // 同样动态分派:a 指向 B 实例 → 仍调用 B.method()⚠️ 注意:原问题中 test2(new B()) 输出 "B"(而非 "A"),并非输出 "A" —— 这是关键纠错点。只要 method() 是 public、非 static、非 final、非 private 的实例方法,且被子类重写,无论参数声明为 In、A 还是 Object,JVM 始终依据实际对象类型调用 B.method()。
验证代码:
public static void main(String[] args) {
test1(new A()); // 输出 "A"
test1(new B()); // 输出 "B" ← 动态分派生效
test2(new B()); // 输出 "B" ← 同样是动态分派,非静态绑定!
}为什么接口能支持动态分派?
接口本身不提供方法实现,但它定义了契约。当一个变量声明为接口类型(如 In i),JVM 并不依赖接口去“找实现”,而是:
- 在运行时获取 i 所引用对象的真实类信息(每个对象头都包含指向 Class 对象的指针);
- 从该类的虚方法表(vtable)中查找 method() 的入口地址;
- 若当前类未重写,则回溯至父类或实现类的 vtable,直至找到首个匹配实现。
这正是 obj.getClass() 可靠返回 B.class 的底层基础——对象永远知道自己是谁。
关键注意事项
- ✅ 动态分派仅适用于 public / protected 实例方法(default 接口方法也适用);
- ❌ 不适用于 static、private、final 方法(它们被静态绑定);
- ❌ 成员变量访问永远是静态分派(字段不参与多态);
- ? JVM 会对热点路径做优化(如内联缓存、单态内联),但语义上仍保证动态分派行为一致;
- ? 这一机制本质是“消息传递”(Message Passing):调用 i.method() 等价于向对象发送 method 消息,由对象自主响应。
总结而言,Java 接口类型的“魔法”不在接口本身,而在于 JVM 对每个对象运行时类型的严格追踪与虚方法表的高效查表机制。开发者只需遵循重写规则,即可自然获得多态能力——无需显式类型检查,亦不牺牲性能。


















