方法调用
确定被调用方法的版本
包括
解析调用
和方法分派
解析调用
对于在那些"编译器可知,运行期不可变"的方法,主要包括静态方法和私有方法。前者与类型直接关联,后者外部不可访问,决定了它们都不能通过继承或者别的方式重写出其它版本,适合在类加载阶段进行解析。
对应的字节码指令:invokestatic,invokespecial
解析调用
是静态的过程,在编译器完全确定,在类加载阶段就会把涉及的符号引用全部转化为直接引用,不必延后到运行期再确定。方法调用还有另一种形式:分派
,包括静态分派
和动态分派
。
分派
分派体现了
多态
的特性
静态分派的主要体现就是
方法重载
-
方法重载是根据
静态类型
确定方法版本的,具体来说,子类对象可以指向父类引用,那么等号左边的父类就是静态类型
,在编译器已知,而等号右边的子类是实际类型
,在运行期才能确定下来。 -
静态分派发生在
编译阶段
。编译器虽然能确定方法的重载版本,但很多情况下并不是唯一的,在存在多个合适版本的情况下,会选择最合适
的版本。重载方法的参数类型可以转化的顺序:自动类型转换——>包装类——>接口——>父类——>变长参数
。
public class Test { static class Human{ } static class Man extends Human{ } static class Woman extends Human{ } public void sayHello(Human human){ System.out.println("hello,gay."); } public void sayHello(Man man){ System.out.println("hello,man."); } public void sayHello(Woman woman){ System.out.println("hello,woman."); } public static void main(String[] args) { Test tx=new Test(); Human man=new Man(); Human woman=new Woman(); tx.sayHello(man); tx.sayHello(woman); } /* hello,gay. hello,gay. */
动态分派
动态分派的主要体现是
方法重写
这种多态性的根源在于虚方法调用指令
invokevirtual
的执行逻辑,invokevirtual
第一步就是确定操作数栈顶第一个元素所指向对象的实际类型,也就是在运行期确定方法接收者的实际类型。
-
方法重写是根据
方法接受者的实际类型
来选择方法版本的 -
字段没有多态性,哪个类的方法访问某个名字的字段时,改名字指的就是这个类能看到的那个字段。
public class Polymorphism { static class Human{ public void show(){ System.out.println("hello,guy"); } } static class Man extends Human{ public void show(){ System.out.println("hello,man"); } } static class Woman extends Human{ public void show(){ System.out.println("hello,woman"); } } public static void main(String[] args) { Human man=new Man(); man.show();//hello,man Human woman=new Woman(); woman.show();// hello,woman man=new Woman(); man.show();// hello,woman } }