#分派=定位要运行的函数
在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上面写:
```
单分派是根据一个宗量对目标方法进行选择;
多分派是根据多于一个宗量对目标方法进行选择;
```
所以
静态分派,考虑了函数名+参数 ,多于一个宗量;
动态分派,考了了实例的类型 ,一个宗量。
- 参考资料
- 容器的实现
- ArrayList、LinkedList与Vector的区别
- Map,Set,List,Queue,Stack的特点与用法
- HashMap的实现
- HashMap和ConcurrentHashMap差别
- HashMap和HashTable的区别
- fast fail
- java 实用方法
- Collections中实用的函数
- ArrayList中实用的函数
- Integer和Character
- Properties类的简单使用
- XML实用解析
- 从jar包中读取文件信息
- java自带base64加密解密
- java机制
- 分派
- 反射
- 类加载机制
- java中一个对象的初始化
- 泛型
- 自动装箱,拆箱与遍历循环
- 偏向于语法
- new int[]
- new boolean[]
- Switch能否用string做参数
- equals与==的区别
- 泛型对象数组
- Enum的用法
- String、StringBuffer与StringBuilder的区别
- try catch finally
- finalize方法
- object有哪些公用方法
- Java的四种引用,强弱软虚,用到的场景
- java访问修饰符
- Hashcode的作用
- 九种基本数据类型
- java对象大小
- 数组长度
- 动态代理的一个例子
- java.lang.NoClassDefFoundError
- ThreadLocal