这里先引用周志明书上对一个例子
public class StaticDispacher{
static abstract class Human{
}
static class Man extends Human{
}
static class Woman extends Human{
}
public void sayHello(Human guy){
System.out.println("hello guy");
}
public void sayHello(Man man){
System.out.println("hello man");
}
public void sayHello(Woman woman){
System.out.println("hello woman");
}
public static void main(String[] args){
Human man = new Man();
Human woman = new Woman();
StaticDispacher sr = new StaticDispacher();
sr.sayHello(man);
sr.sayHello(woman);
}
}
程序运行结果 为 hello guy
hello guy
我们看下字节码 信息:
26: invokevirtual #13 // Method sayHello:(LStaticDispacher$Human;)V
29: aload_3
30: aload_2
31: invokevirtual #13 // Method sayHello:(LStaticDispacher$Human;)V
可以看到,在编译生成class后,在mian中调用对两个sayHello方法对参数都是Human。
静态分配发生在编译时期,对应于java的重载,即java中方法重载时,决定调用哪一个是根据编译时期d静态类型对而不是运行时类型。
动态分配:和重写Override有着密切的关系,看一下例子
public class DynamicDispatch{
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello(){
System.out.println("hello man");
}
}
static class Woman extends Human{
@Override
protected void sayHello(){
System.out.println("hello woman");
}
}
public static void main(String[] args){
Human man = new Man();
Human woman = new Woman();
man.sayHello();
woman.sayHello();
man = new Woman();
man.sayHello();
}
}
方法的输出就不说了,我们看下字节码:
16: aload_1
17: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V
20: aload_2
21: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V
24: new #4 // class DynamicDispatch$Woman
27: dup
28: invokespecial #5 // Method DynamicDispatch$Woman."<init>":()V
31: astore_1
32: aload_1
33: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V
36: return
和上次一行,还是invokevirtual指令,但是这里会产生不同的效果,这和invikevirtual指令的多态查找有关,简单的说就是对操作数栈顶的元素,会找到操作数栈顶的第一个元素的引用指向的对象的实际类型,若此引用指向的数据和常量池中内容相符合,则返回这个直接引用,否则按照继承关系从下往上依次寻找,这样就定位到了方法的实际执行者。