我有一般情况的代码:
public class A {
public String show(A obj) {
return ("A and A");
}
}
public class B extends A {
public String show(B obj) {
return ("B and B");
}
public String show(A obj) {
return ("B and A");
}
}
public class C extends B {
}
public class Test {
public static void main(String[] args) {
A a = new B();
B b = new B();
C c = new C();
System.out.println("1--" + a.show(b));
System.out.println("2--" + a.show(c));
}
}
结果是:
1--B and A
2--B and A
我知道Java中存在从高到低的优先级链:
this.show(O), super.show(O), this.show((super)O), super.show((super)O)
我的理解如下:
在这段代码中:
A a = new B()
发生了一场*. A是父类引用,B是子父类引用.编译并运行代码时,子父类引用确定如何选择方法.在这种情况下,选择B类中的show(A).
还需要多态性必须满足:
选择的方法应包含在父类定义中.
有人可以对显示的结果做出更详细的解释吗?
解决方法:
为了得到结果B和A两次的原因,你需要知道这有两个部分:编译和运行时.
汇编
遇到语句a.show(b)时,编译器会采取以下基本步骤:
>查看在(a)上调用该方法的对象并获取其声明的类型.这种类型是A.
>在A类及其所有超类型中,列出名为show的所有方法.编译器只会找到show(A).它没有看B或C中的任何方法.
>从找到的方法列表中,选择与参数(b)最匹配的方法(如果有). show(A)将接受b,因此选择此方法.
传递c的第二个调用也会发生同样的事情.前两个步骤是相同的,第三步将再次找到show(A),因为只有一个,它也匹配参数c.因此,对于您的两个调用,其余过程都是相同的.
一旦编译器弄清楚它需要什么方法,它将创建一个字节码指令invokevirtual,并将解析后的方法show(A)作为它应该调用的方法(如Eclipse中所示,打开.class):
invokevirtual org.example.A.show(org.example.A) : java.lang.String [35]
运行
运行时,当它最终到达invokevirtual时,还需要执行几个步骤.
>获取调用该方法的对象(此时已经在堆栈中),这是一个.
>查看此对象的实际运行时类型.由于a = new B(),此类型为B.
>查看B并尝试找到方法show(A).由于B覆盖它,因此找到此方法.如果不是这种情况,它会查找超类(A和Object),直到找到这样的方法.重要的是要注意它只考虑show(A)方法,例如.从未考虑过来自B的节目(B).
>运行时现在将从B调用方法show(A),给出字符串B和A作为结果.
关于此的更多细节在spec for invokevirtual
中给出:
If the resolved method is not signature polymorphic (§2.9), then the invokevirtual instruction proceeds as follows.
Let C be the class of objectref. The actual method to be invoked is selected by the following lookup procedure:
If C contains a declaration for an instance method m that overrides (§5.4.5) the resolved method, then m is the method to be invoked, and the lookup procedure terminates.
Otherwise, if C has a superclass, this same lookup procedure is performed recursively using the direct superclass of C; the method to be invoked is the result of the recursive invocation of this lookup procedure.
Otherwise, an AbstractMethodError is raised.
对于您的示例,objectref是a,其类是B,并且已解析的方法是来自invokevirtual的方法(显示(A)来自A)
tl:dr – 编译时确定要调用的方法,运行时确定从哪里调用它.