实例变量调用在你做什么时工作A thing = new B();其中B是A的一个子类?

问题描述:

这可能是回答某处,但我不知道要搜索什么。想象一下,你有以下...实例变量调用在你做什么时工作A thing = new B();其中B是A的一个子类?

超类,Animal.java

public class Animal { 
    public String noise = "squeak"; 
    public String toString() { return noise; } 
} 

子类,Lion.java

public class Lion extends Animal { 
    public String noise = "ROAR!!"; 
    public String toString() { return noise; } 
} 

的主类,Runner.java

public class Runner { 
    public static void main(String[] args) { 
    Animal a = new Animal(); 
    System.out.println(a); 
    System.out.println(a.noise); 
    Lion b = new Lion(); 
    System.out.println(b); 
    System.out.println(b.noise); 
    Animal c = new Lion(); 
    System.out.println(c); 
    System.out.println(c.noise); 
    } 
} 

输出结果为:

squeak 
squeak 
ROAR!! 
ROAR!! 
ROAR!! 
squeak 

c.noise为什么会返回吱吱声?实例方法调用和实例变量之间的区别是什么,它会返回您期望的结果,而另一个则不会?为什么Java会这样做?

感谢

+1

我不是Java专业人员,所以我不会称这为答案,但区别在于Java中的方法是虚拟的。也就是说,当你调用c.toString()时,Java通过使用查找表知道使用正确的方法是Lion的toString()。但是,这种虚拟化不会发生在实例变量上。 – erisco

简短的回答:

您可以替代的方法,但它不可能覆盖领域。

龙答:

每个班级看到的方法和它自己和它的父母的领域(除私有方法)。如果孩子在名称相同的情况下删除方法在其父类中的名称,则此方法将变为覆盖 - 如果以某种方式在子实例上调用此方法(即使是从父方法),将使用全新的方法而不是父级的方法。孩子仍然可以通过电话super.method(...)致电最后一位家长的原始方法。

但是当我们来到田野时,故事是不同的。如果孩子声明了一个新的字段,该字段的名称与父类中的字段完全一样,它将简单地隐藏父类的字段不重写,就像本地变量隐藏全局字段一样。所以子方法只会看到孩子的字段,但父母的方法会继续看到父母的字段,并且父类的任何方式都无法看到孩子的字段 - 这就是你所得到的。

孩子可以通过((Parent)this).field访问其父母的字段。

+0

当他们说“类方法/字段”时,大多数人的意思是静态方法/字段,但我很确定你是指实例方法/字段。 –

+0

对不起,为了避免含糊不清,更正。 –

较长的答案:

所以真的你会做到这一点的方法是这样定义的狮子:所以现在

public class Lion extends Animal { 
    public Lion() { 
    noise = "ROAR!!"; 
    } 
} 

的狮子实例动物的噪音成员变量已经更新到吼!

当然,你(几乎)从来没有在类似野外的类上实际拥有一个公共可变成员。

您无法覆盖字段,中的noise的新声明会隐藏父项的noise属性。像这样:

public class Lion extends Animal { 

// public String noise = "ROAR!!"; // <---- Remove this line 

    public Lion() { 
     noise = "ROAR"; 
    } 

    public String toString() { 
     return noise; 
    } 
} 

java中的所有非静态方法默认为"virtual functions"。除非它们被标记为final(这使得方法不可覆盖)。 Java使用virtual method table来调用正确的对象的方法。

这是因为Java只谈约方法覆盖。成员变量只能在子类中映射。所以当你说c.noise时,它实际上是指父类中的字符串变量作为c,如果引用类型为Animal。

您感兴趣的主题有Dynamic DispatchVirtual Method Table。基本上,通过设计,Java允许重写方法(假设它们是非最终的),并且在运行时JVM将执行相应的实现。这个多态属性只提供给方法。好的OO设计将决定封装这些字段。