40

我对此Java代码有一些疑问。它给出的输出是“毛茸茸的布雷”。我的问题:

  1. 为什么我会得到这个输出?
  2. 如何访问 ZooKeeper 类中的字符串对象引用“名称”?
  3. 如果它与变量阴影有关,那么哪个变量被阴影?

代码:

class Mammal {
   String name = "furry ";
   String makeNoise() { return "generic noise"; }
 }
 class Zebra extends Mammal {
   String name = "stripes ";
   String makeNoise() { return "bray"; }
 }
 public class ZooKeeper {
   public static void main(String[] args) { new ZooKeeper().go(); }
   void go() {
     Mammal m = new Zebra();
     System.out.println(m.name + m.makeNoise());
     //Output comes as "furry bray". Please explain this.
     //And how can we access the name variable, the one having "stripes " in it.
     //Does it have something to do with Variable Shadowing?
   }
 }
4

5 回答 5

63

变量不是多态的。当您访问m.name时,它将始终使用作为Mammal.name该对象一部分的字段,而不管对象的执行时间类型如何。如果您需要访问Zebra.name,则需要一个编译时类型为的表达式Zebra

makeNoise方法实际上是调用的——执行时使用的实现确实取决于对象的类型。

请注意,如果您将所有字段设为私有(无论如何这通常是个好主意),这最终不会成为问题。

这实际上是隐藏而不是阴影。有关隐藏的详细信息,请参阅JLS 第 8.3节,有关阴影的详细信息,请参阅第 6.4.1 节。我不能说我总是保持差异...

于 2013-06-05T13:29:06.220 回答
15

输出为“毛茸茸的布雷”。请解释一下。

fields在 java 程序中不通过动态查找访问。相反,它们在编译时被静态解析。这就是为什么你要furrym.name. 而methods在 Java 中,程序是通过动态查找来访问的。这就是为什么你要braym.makeNoise().

我们如何访问名称变量,其中包含“条纹”?

如果你想访问Zebra.name,你应该输入 cast mto 'Zebra'。这看起来像这样:

System.out.println(((Zebra)m).name + m.makeNoise());

更新
这里展示的现象是字段隐藏而不是变量阴影的结果。

于 2013-06-05T13:34:09.643 回答
9

Java 中没有变量覆盖,您在父项和子项中都声明了名称,但您通过父引用变量引用它。这就是你得到“毛茸茸”的原因。

方法有覆盖,这就是为什么你得到了 bray。因为在运行时它查看了堆中的真实对象,发现它是 Zebra。

于 2013-06-05T13:30:29.410 回答
4

Java 中的变量名由引用类型解析,而不是它们所引用的对象。因此,m.name 指的是 Mammal 中的变量名,即使面团 m 是 Zebra。

于 2013-06-05T13:30:43.950 回答
3

发生这种情况是因为您的字段namefromMammal只是被字段 from隐藏。然后,您可以通过转换为所需的类型来访问它。 另一方面,方法会覆盖父级的相同方法,因此您无法再从父级访问实现。nameZebra
makeNoise()

于 2013-06-05T13:31:43.533 回答