ThinkSSL🔒 一键申购 5分钟快速签发 30天无理由退款 购买更放心 广告
#分派=定位要运行的函数 在java的代码中,根据 **函数名 + 参数类型** 可以对某一个函数的描述。 在一个类里,一个函数的描述,可以唯一确定一个函数。 如: ``` class Test{ public void test(){ System.out.println("test"); } public void fun(){ System.out.println("fun"); } public void fun(int a){ System.out.println("fun(int a)"); } public static void main(String[] args){ Test test =new Test(); test.test(); test.fun(); test.fun(2); } } ``` 上面的示例中,只要根据函数名称,以及参数的类型就可以唯一确定一个函数。(如果函数名和参数一样,会无法编译)而函数名一样,但是参数不一样,这种情况称为【重载】 ##这里本文定义: ###【函数描述】 = 函数名 + 参数类型 但是,存在一个情况:父类。 由于java允许继承,继承可以继承父类的函数,那么如果一个子类和父类出现了相同的【函数描述】,这种情况称为【重写】 ``` 思考: java完全可以设计成,冲突就编译不成功。 但是这样肯定没有可以编译来得灵活,每个方法都要起不一样的名字。不能不同对象执行不同的方法。 ``` java的解决方式是【动态分派】。在执行时,才知道要执行哪个类中满足的具有该【函数描述】方法。 也就是说,代码一开始,【函数描述】是确定的,差别只在于类的不同。 所以,重载和重写 = 【函数描述】 + 实例类 只要通过这两个元素,就可以唯一确定一个真正要执行的函数。 本文所说的【函数描述】被人称为【静态分派】; 本文所说的找到【实例类】被人称为【动态分派】 >下文参考来源: 文/会行走的两脚书橱(简书作者) 原文链接:http://www.jianshu.com/p/95620f43c89a 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。(其实我看也是参考下面书本的217页的内容) <br>以及page 209 《深入理解java虚拟机》第一版 #静态分派 根据上面的描述:根据[【函数描述】,直接在编译器写进代码里了。 https://my.oschina.net/sel/blog/215959 (这个文章里有一些更细的解释吧,1.我觉得我前面说的很清楚,所以不用贴;2.其实也是根据《深入理解java虚拟机》写的,示例基本一样209-214 #动态分派 ``` public class DynamicDispatch { public static void main(String[] args) { Human man = new Man(); Human woman = new Woman(); man.sayHello(); // Output : man say hello woman.sayHello(); // Output : woman say hello } private static abstract class Human { protected abstract void sayHello(); } private static class Man extends Human { protected void sayHello() { System.out.println("man say hello"); } } private static class Woman extends Human { protected void sayHello() { System.out.println("woman say hello"); } } } ``` 编译过后所得的字节码文件如下: ``` (常量池的部分内容) Constant pool: #1 = Methodref #8.#23 // java/lang/Object."<init>":()V #2 = Class #24 // DynamicDispatch$Man #3 = Methodref #2.#25 // DynamicDispatch$Man."<init>":(LDynamicDispatch$1;)V #4 = Class #26 // DynamicDispatch$Woman #5 = Methodref #4.#25 // DynamicDispatch$Woman."<init>":(LDynamicDispatch$1;)V #6 = Methodref #13.#27 // DynamicDispatch$Human.sayHello:()V ``` ``` public class DynamicDispatch { public DynamicDispatch(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class DynamicDispatch$Man 3: dup 4: aconst_null 5: invokespecial #3 // Method DynamicDispatch$Man."<init>":(LDynamicDispatch$1;)V 8: astore_1 9: new #4 // class DynamicDispatch$Woman 12: dup 13: aconst_null 14: invokespecial #5 // Method DynamicDispatch$Woman."<init>":(LDynamicDispatch$1;)V 17: astore_2 18: aload_1 // 将第二个引用类型本地变量(man)推送至操作数栈栈顶 19: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V,调用#6代表的实例方法,并且方法的接收者就是操作数栈顶元素 22: aload_2 23: invokevirtual #6 // Method DynamicDispatch$Human.sayHello:()V 26: return } ``` 在main函数中,0~8是创建Man对象并赋到man,9~17是创建woman对象并赋到woman,18就是将man对象压入操作数栈栈顶,接下来19调用Human的sayHello方法,执行的字节码指令是invokevirtual,其具有多态查找过程,invokevirtual在运行时的解析过程大致为: >1. 找到操作数栈顶的第一个元素所指向的对象的实际类型,记为C。 >2. 如果在类型C中找到与常量中的描述符合简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;如果权限校验不通过,返回java.lang.IllegalAccessError异常。 >3. 否则,按照继承关系从下往上一次对C的各个父类进行第2步的搜索和验证过程。 >4. 如果始终没有找到合适的方法,则抛出 java.lang.AbstractMethodError异常。 #单分派 和 多分派 page 128上面写: ``` 单分派是根据一个宗量对目标方法进行选择; 多分派是根据多于一个宗量对目标方法进行选择; ``` 所以 静态分派,考虑了函数名+参数 ,多于一个宗量; 动态分派,考了了实例的类型 ,一个宗量。