我有一般情况的代码:
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));
}
}
Run Code Online (Sandbox Code Playgroud)
结果是:
1--B and A
2--B and A
Run Code Online (Sandbox Code Playgroud)
我知道Java中有一个从高到低的优先级链:
this.show(O), super.show(O), this.show((super)O), super.show((super)O)
Run Code Online (Sandbox Code Playgroud)
我的理解如下:
在这段代码中:
A a = new B()
Run Code Online (Sandbox Code Playgroud)
发生了上行。A 是父类引用,B 是子父类引用。当代码被编译和运行时,子父类引用决定了如何选择方法。在这种情况下,show(A)选择 B 类。
还有一个多态必须满足的要求:选择的方法应该包含在父类定义中。
有人可以对显示的结果给出更详细的解释吗?
为了弄清楚为什么会得到B and A两次结果,您需要知道这有两个部分:编译和运行时。
汇编
\n当遇到语句 时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]\nRun Code Online (Sandbox Code Playgroud)\n运行
\n运行时,当它最终达到invokevirtual需要时还需要执行几个步骤。
a。a = new B(),这种类型是B。B并尝试找到方法show(A)。由于覆盖了该方法,所以找到了该方法B。如果情况并非如此,它将在超类(A和Object)中查找,直到找到这样的方法。值得注意的是,它只考虑show(A)方法,因此例如。show(B)fromB从未被考虑过。show(A)来自 的方法B,给出String B and A结果。有关此内容的更多详细信息在规范invokevirtual中给出:
\n\n如果解析的方法不是签名多态 (\xc2\xa72.9),则 invokevirtual 指令将按如下方式进行。
\n设 C 为 objectref 的类。实际要调用的方法是通过以下查找过程选择的:
\n如果 C 包含覆盖 (\xc2\xa75.4.5) 已解析方法的实例方法 m 的声明,则 m 是要调用的方法,并且查找过程终止。
\n否则,如果 C 有超类,则使用 C 的直接超类递归执行相同的查找过程;要调用的方法是该查找过程的递归调用的结果。
\n否则,将引发 AbstractMethodError。
\n
对于您的示例,objectrefis a,其类是B,解析的方法是invokevirtual( show(A)from A)中的方法
tl:dr - 编译时决定调用什么方法,运行时决定从哪里调用它。
\n| 归档时间: |
|
| 查看次数: |
211 次 |
| 最近记录: |