与静态分派不同滴是,动态分派Java虚拟机是根据实际类型来分派方法执行版本。典型例子就是方法的重写。
看代码:
package diptch;
public class DynamicDispatch {
/**
*
* @author Administrator
*
*/
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{
@Override
protected void sayHello() {
System.out.println("man say hello!");
}
}
static class Woman extends Human{
@Override
protected void sayHello() {
System.out.println("woman say hello!");
}
}
public static void main(String[] args) {
Human man=new Man();
Human woman=new Woman();
man.sayHello();
woman.sayHello();
man=new Woman();
man.sayHello();
}
}
结果:
man say hello!
woman say hello!
woman say hello!
拔不出所料赛,结果跟我们想滴一毛毛一样。来看看字节码指令。
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: new #16 // class diptch/DynamicDispatch$Ma
n
3: dup
4: invokespecial #18 // Method diptch/DynamicDispatch$M
an."<init>":()V
7: astore_1
8: new #19 // class diptch/DynamicDispatch$Wo
man
11: dup
12: invokespecial #21 // Method diptch/DynamicDispatch$W
oman."<init>":()V
15: astore_2
16: aload_1
17: invokevirtual #22 // Method diptch/DynamicDispatch$H
uman.sayHello:()V
20: aload_2
21: invokevirtual #22 // Method diptch/DynamicDispatch$H
uman.sayHello:()V
24: new #19 // class diptch/DynamicDispatch$Wo
man
27: dup
28: invokespecial #21 // Method diptch/DynamicDispatch$W
oman."<init>":()V
31: astore_1
32: aload_1
33: invokevirtual #22 // Method diptch/DynamicDispatch$H
uman.sayHello:()V
36: return
我们分别看21,28,33行的指令都用的是invokevirtual指令,先说这个指令的特点:
它的执行分为三步:
1.找到操作数栈顶的第一个元素所指向的对象的实际类型。举个栗子:在我们的20行我们取出了在局部变量表中存储的实际类型儿,就是new出来的women实例。
2.如果当前实际类型的类中中找到与常量中的描述符和简单名称相符合的方法,然后进行访问权限验证,如果验证通过则返回这个方法的直接引用,查找过程结束;如果验证不通过,则抛出java.lang.IllegalAccessError异常。白话就是在当前类找正在执行的荡秋千方法,找到就直接引用。
3.、否则未找到,就按照继承关系从下往上依次对各个父类进行第2步的搜索和验证过程。
看完这个字节码指令的执行过程,我们就知道了是如何寻找实际类型的了,
invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不同的直接引用上,这个过程就是Java语言方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
所以虽然我们看到
21: invokevirtual #22 // Method diptch/DynamicDispatch$H
uman.sayHello:()V
这样的字节码注释的是Human.sayHello:()V 表面上市静态类型,但是由于是invokevirtual 指令来操作的,所以俺们实际执行的是当前实际类型的方法。