分派
2017-11-06 10:36
99 查看
静态分派和动态分派
静态分派案例:public class fenpai { static abstract class A { } static class B extends A { } static class C extends A { } public static void sayHi(A a) { System.out.println("hi ,father"); } public void sayHi(B b) { System.out.println("hi ,sister"); } public void sayHi(C c) { System.out.println("hi ,brother"); } public static void main(String[] args) { fenpai f = new fenpai(); A a1 = new B(); A a2 = new C(); f.sayHi(a1); f.sayHi(a2); } }
hi ,father hi ,father
动态分派案例:
public class fenpai { static abstract class A { public void sayHi() { System.out.println("hi ,father"); } } static class B extends A { @Override public void sayHi() { System.out.println("hi ,father"); } } static class C extends A { @Override public void sayHi() { System.out.println("hi ,sister"); } } public void sayHi(C c) { System.out.println("hi ,brother"); } public static void main(String[] args) { fenpai f = new fenpai(); A a1 = new B(); A a2 = new C(); a1.sayHi(); a2.sayHi(); } }
hi ,father hi ,sister
解释一下两种结果不同的原因:
父类A称为变量的静态类型,也叫外观类型,子类BC称为实际类型,静态类型是编译期可知的,而实际类型是运行期才知道。
结果不同的原因是:
静态分配的虚拟机重载时,是通过参数的静态类型而不是实际类型进行判定的,编译器在编译阶段,直接根据静态类型决定使用哪个版本。
而动态分配是方法的重写,Java虚拟机是通过实际类型来判断执行的版本。
让我们来看一下他们的二进制字节码来了解编译器干了什么?
静态分配
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: new #1 // class fenpai 3: dup 4: invokespecial #44 // Method "<init>":()V 7: astore_1 8: new #45 // class fenpai$B 11: dup 12: invokespecial #47 // Method fenpai$B."<init>":()V 15: astore_2 16: new #48 // class fenpai$C 19: dup 20: invokespecial #50 // Method fenpai$C."<init>":()V 23: astore_3 24: aload_2 25: invokestatic #51 // Method sayHi:(Lfenpai$A;)V 28: aload_3 29: invokestatic #51 // Method sayHi:(Lfenpai$A;)V 32: return LineNumberTable: line 24: 0 line 25: 8 line 26: 16 line 27: 24 line 28: 28 line 29: 32 LocalVariableTa
动态分配
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=4, args_size=1 0: new #1 // class fenpai 3: dup 4: invokespecial #34 // Method "<init>":()V 7: astore_1 8: new #35 // class fenpai$B 11: dup 12: invokespecial #37 // Method fenpai$B."<init>":()V 15: astore_2 16: new #38 // class fenpai$C 19: dup 20: invokespecial #40 // Method fenpai$C."<init>":()V 23: astore_3 24: aload_2 25: invokevirtual #41 // Method fenpai$A.sayHi:()V 28: aload_3 29: invokevirtual #41 // Method fenpai$A.sayHi:()V 32: return LineNumberTable: line 28: 0 line 29: 8 line 30: 16 line 31: 24 line 32: 28 line 33: 32
很清楚地看到静态分配的25行 29行
invokestatic
动态分配的25行,29行
invokevirtual
这就是区别所在。
a)静态分配在编译器就决定了使用哪个重载版本,所以他把这个决定的重载方法写入了invokestatic指令中。
b)动态分配,编译器不知道实际类型,所以他不知道执行哪个重写版本,于是它只能写入invokevirtual指令中。
invokevirtual指令执行过程:
1)确定接受者的实际类型。
2)在操作数栈找到对应这个实际类型。
3)在类型中找到与常量中描述符合和简单名称都相同的方法,进行访问权限检验,如果通过,则返回这个方法的直接引用,如果不通过,返回java.lang.IllegalAccessError 异常。
4)否则,按照继承关系从上往下对这个实际类型的各个父类进行第三步的搜索和验证功能。
5)如果没有合适的方法,则抛出java.lang.AbstractMethodError。
相关文章推荐
- WINX的消息分派机制(终结篇)
- JBPM的任务分派机制
- WINX的消息分派机制
- Tapestry5 事件分派机制
- 重定向和请求分派的比较
- Servlet中,重定向和请求分派的区别!!!
- 通过对系统分派表的直接还原,防御内核本地API钩子(翻译) 分享
- 访问者模式讨论篇:java的动态绑定与双分派
- 访问者模式讨论篇:java的动态绑定与双分派
- java的动态绑定与双分派(规避instanceof)
- GCD10: 用GCD构建自己的分派队列
- 也谈double dispatch(双分派)::Visitor 模式(转)
- 静态分派和动态分派
- Java设计模式(三) Visitor(访问者)模式及多分派场景应用
- Double Dispatch(双分派)(c++ 版)
- Tapestry5 事件分派机制
- java 模拟斗地主分派
- 【深入Java虚拟机】之五:多态性实现机制——静态分派与动态分派
- 静态分派与动态分派
- Java设计模式(三) Visitor(访问者)模式及多分派场景应用