main 方法的执行结果:
human
human
human
虽然 StaticDispatch 为每种 Human 的子类都重载了一个 say 方法,但是由于重载采用的是静态分派,是根据对象的静态类型做方法匹配的。所以结果全都匹配到了 public void say(Human human) 方法。main 方法编译之后的字节码:
public static main([Ljava/lang/String;)V NEW method_invoke/StaticDispatch$Man DUP INVOKESPECIAL method_invoke/StaticDispatch$Man.<init> ()V ASTORE 1 NEW method_invoke/StaticDispatch$Woman DUP INVOKESPECIAL method_invoke/StaticDispatch$Woman.<init> ()V ASTORE 2 NEW method_invoke/StaticDispatch$Child DUP INVOKESPECIAL method_invoke/StaticDispatch$Child.<init> ()V ASTORE 3 NEW method_invoke/StaticDispatch DUP INVOKESPECIAL method_invoke/StaticDispatch.<init> ()V ASTORE 4 // 下面为调用 say ALOAD 4 ALOAD 1 INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V ALOAD 4 ALOAD 2 INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V ALOAD 4 ALOAD 3 INVOKEVIRTUAL method_invoke/StaticDispatch.say (Lmethod_invoke/StaticDispatch$Human;)V RETURN
从字节码也能看到,编译器确实是按照静态分派选择了匹配静态类型的 StaticDispatch.say(LStaticDispatch$Human;)V 方法,而没有按照变量的实际类型去匹配重载的方法。
public class Overload {
public static void out(char a) { System.out.println("char " + a); }
public static void out(int a) {System.out.println("int " + a);}
public static void out(long a) { System.out.println("long " + a); }
public static void out(float a) { System.out.println("float " + a); }
public static void out(double a) { System.out.println("double " + a); }
public static void out(Integer a) { System.out.println("integer"); }
public static void out(Character a) { System.out.println("character"); }
public static void out(Serializable a) { System.out.println("serializable " + a); }
public static void out(Comparable a) { System.out.println("comparable " + a); }
public static void out(Object a) { System.out.println("object " + a); }
public static void out(char... a) { System.out.println("char ... " + Arrays.toString(a)); }
public static void main(String[] args) {
out('c');
}
}
这段代码也是一个静态分派的例子,编译器会选择参数类型做合适的函数去调用。可以注释掉所有 out 函数,留下 out(Serializable a),你会发现程序也能成功编译和运行。如果留下Serializeable 和 Comparable 编译则会失败,提示对 out 的引用不明确。
动态分派
根据变量的「实际类型」匹配调用方法的过程称为动态分派。发生的场景为方法重写。当调用一个可能被子类重写或继承的方法时,就会触发动态分派。
public class DynamicDispatch {
static class Human {
public void say() {
System.out.println("human");
}
}
static class Man extends Human {
@Override
public void say() {
System.out.println("man");
}
}
static class Woman extends Human {
@Override
public void say() {
System.out.println("woman");
}
}
}










