2

我正在练习考试,发现了一个让我完全迷失的示例问题。对于以下代码,找到输出是什么:

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 时,我得到了完全不同的东西:

卷曲 1
卷曲 2
拉里 1
拉里 1
卷曲 1
卷曲 1
卷曲 1
卷曲 2
卷曲 3
卷曲 3
卷曲 1
卷曲 2
拉里 2
拉里 2
拉里 1

前几个还可以,后来真的看不懂了。有人对这个问题有很好的解释吗?

谢谢

4

3 回答 3

5

我会从画图开始...

Moe - print(Moe)
 |
Larry - print(Moe), print(Larry)
 |
Curly - print(Moe), print(Larry), print(Curly)

然后我会跟踪变量:

  • 拉里 - stooge1 -> 卷毛
  • 萌 - stooge2 -> 拉里
  • 萌 - 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());

编译器说:

  • 拉里可以分配到 stooge2 吗?(是的,因为拉里是萌的子类)
  • Moe 有 print(Moe) 方法吗?(是的)

运行时说:

  • 我应该在此对象上调用 print(Moe) 方法... stooge2
  • stooge2 指向拉里。
  • 我将在 Larry 类中调用 print(Moe) 方法。

一旦你完成了所有这些尝试摆脱一些方法,看看它如何改变事情。

于 2009-03-16T01:06:17.840 回答
2

实际上,这个问题并不像看起来那么简单,因为 Java 既是静态的又是动态的。您必须先了解每个应用程序的应用位置,然后才能了解您从本练习中获得的所有结果。

TofuBeer 提到的一般规则只在动态绑定的情况下是正确的。在静态绑定中,仅在编译时做出决定。

您的示例混合了动态绑定(当方法被覆盖时)和静态绑定(当方法被重载时)。

请查看此问题以获取更多详细信息。

于 2009-03-16T13:36:14.307 回答
0

提示是在查看对象时忽略左侧的值。而是在声明的时候看一下right的值,这是对象的实际值。

于 2009-03-16T01:06:11.927 回答