方法调用
确定被调用方法的版本
包括
解析调用和方法分派
解析调用
对于在那些"编译器可知,运行期不可变"的方法,主要包括静态方法和私有方法。前者与类型直接关联,后者外部不可访问,决定了它们都不能通过继承或者别的方式重写出其它版本,适合在类加载阶段进行解析。
对应的字节码指令: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 } }