-
19-08-2019 - |
题
我正在准备考试,发现了一个让我完全迷失的示例问题。对于以下代码,找出输出是什么:
class Moe {
public void print(Moe p) {
System.out.println("Moe 1\n");
}
}
class Larry extends Moe {
public void print(Moe p) {
System.out.println("Larry 1\n");
}
public void print(Larry l) {
System.out.println("Larry 2\n");
}
}
class Curly extends Larry {
public void print(Moe p) {
System.out.println("Curly 1\n");
}
public void print(Larry l) {
System.out.println("Curly 2\n");
}
public void print(Curly b) {
System.out.println("Curly 3\n");
}
}
public class Overloading_Final_Exam {
public static void main (String [] args) {
Larry stooge1 = new Curly();
Moe stooge2 = new Larry();
Moe stooge3 = new Curly();
Curly stooge4 = new Curly();
Larry stooge5 = new Larry();
stooge1.print(new Moe());
((Curly)stooge1).print(new Larry());
((Larry)stooge2).print(new Moe());
stooge2.print(new Curly());
stooge3.print(new Curly());
stooge3.print(new Moe());
stooge3.print(new Larry());
((Curly)stooge3).print(new Larry());
((Curly)stooge3).print(new Curly());
stooge4.print(new Curly());
stooge4.print(new Moe());
stooge4.print(new Larry());
stooge5.print(new Curly());
stooge5.print(new Larry());
stooge5.print(new Moe());
}
}
我心里有我的想法,但是当我运行 java 时,我得到了完全不同的东西:
Curly 1 Curly 2 Larry 1 Larry 1 Curly 1 Curly 1 Curly 1 Curly 2 Curly 3 Curly 3 Curly 1 Curly 2 Larry 2 Larry 2 Larry 1
前面几个还好,后面就实在看不懂了。有人对这个问题有很好的解释吗?
谢谢
解决方案
我会从画一幅画开始......
Moe - print(Moe)
|
Larry - print(Moe), print(Larry)
|
Curly - print(Moe), print(Larry), print(Curly)
然后我会跟踪变量:
- 拉里 - 傀儡1 -> 卷毛
- Moe - 傀儡2 -> 拉里
- 萌 - stooge3 -> 卷毛
- 卷毛 - stooge4 -> 卷毛
拉里 - stooge5 -> 拉里
stooge1.print(new Moe())
- stooge1 -> Curly 所以调用 Curly.print(Moe)
((Curly)stooge1).print(new Larry());
- stooge1 -> Curly 所以调用 Curly.print(new Larry())
((Larry)stooge2).print(new Moe());
- stooge2 -> Larry 如此调用 Larry.print(new Moe());
stooge2.print(new Curly());
好吧,这就是事情变得有点棘手的地方(抱歉我之前在这里停止了一个)- stooge2 被宣布为 Moe。因此,当编译器考虑调用什么时,它将调用 print(Moe) 方法。然后在运行时它知道 stooge2 是 Larry,因此它调用 Larry.print(Moe) 方法。
ETC...
如果一直遵循这一点对您来说不成功,请告诉我。
(更新以澄清下一个)
所以一般规则是:
- 编译器查看变量类型来决定调用什么方法。
- 运行时查看变量指向的实际类来决定从哪里获取方法。
所以当你有:
Moe stooge2 = new Larry();
stooge2.print(new Moe());
编译器说:
- Larry 可以被分配到 stooge2 吗?(是的,因为 Larry 是 Moe 的子类)
- Moe 有 print(Moe) 方法吗?(是的)
运行时说:
- 我应该在这个对象上调用 print(Moe) 方法......傀儡2
- stooge2 指着 Larry。
- 我将调用 Larry 类中的 print(Moe) 方法。
一旦你完成了所有这些工作,尝试摆脱一些方法,看看这会如何改变事情。
其他提示
其实,这个问题并不像看起来那么简单,因为Java是静态和动态绑定。你必须了解每个应用之前,你会明白你是从这项工作取得的成果。
通过TofuBeer提到的一般规则是仅在动态绑定的情况下是正确的。在静态绑定,决定仅在编译时由
您例如混合动力结合(当方法被覆盖)和静态结合(当方法被过载)。
一个提示是在看物体时忽略左侧的值。相反,看的声明中右侧的值,这是该对象的实际值。
不隶属于 StackOverflow