重载选择(静态分派),解析调用与动态分派(多态)的特性研究
2019-04-22 本文已影响0人
树心图物
根据周志明《深入理解java虚拟机》所述,国内把重载方法的选择过程叫做静态分派,除此之外是解析调用和动态分派。个人认为这种叫法并不是很好,因为重载选择(静态分派)是在编译成字节码后,也就是编译期时就确定了,而且“重载选择“和“解析调用”有一定的概念上的交叉,没有交叉的概念是解析调用和动态分派,重载选择的概念在java源代码中存在,到字节码后已经完成选择,在字节码级别可以根据调用指令(有一个例外)来区分解析调用和动态分派。
在字节码级别,invokespecial,invokestatic调用一定是解析调用,在编译期就确定了调用哪个方法,不会有变更。final修饰的方法虽然用invokevirtual调用,但是因为不会被重写,也是编译期确定的,所以也是解析调用(只有这个例外)。
在字节码级别,invokevirtual调用非final方法,invokeinterface调用方法是动态分派。
方法接收者:比如 People p = new Boy(); p.sayHello(); p就是接收者。p的静态类型为People,实际类型为Boy。
方法的调用者:调用方法的语句所在上下文那个方法
重载选择的一些特性:
- 根据接收者的静态类型和方法参数的静态类型来确定选择哪个方法
- 方法参数的静态类型匹配有相应的准确性原则,不是严格要求静态类型一定要完全对应的,若找不到完全对应的方法,会提升传入实参的类型去找到最“准确”的方法。比如 hello(People p)这个方法,若传入一个Child类型的参数而且Child继承People,那么在没有Hello(Child c)方法的情况下,会选择hello(People p)
- 可变长度参数的方法也会找最“准确”的方法,可变参数本身优先级低。比如存在hello(String s)和hello(String... ss),在调用hello("world")时会选择更”准确“的hello(String s)方法
- 重载选择还要注意方法接受者的静态类型,若方法接受者静态类型中完全找不到对应的方法,会报错,不会在接受者静态类型的子孙类中寻找