继承和类成员

4

给定:

class A
{
    String s = "A";
}

class B extends A
{
    String s = "B";
}

public class C
{
    public static void main(String[] args){ new C().go();}
    void go()
    {
        A a = new B();
        System.out.println(a.s);
    }
}

问题:

当运行这段代码时,JVM的机制是什么?为什么a.s打印出来是"A"。


除了其他的回应之外,需要注意的是,如果不将a向下转型为B,例如((B)a),则无法访问B的s成员。 - Miserable Variable
4个回答

4

字段引用不受多态性影响,因此在编译时,编译器将引用A的字段,因为你的局部变量是A类型。

换句话说,字段的行为就像Java方法的重载行为,而不是Java方法的覆盖行为。


谢谢您的回答。如果可以的话,我想从您的回答中澄清以下问题:[1]在运行时和编译时,“a”保存的内容是否有区别?[2]在代码的哪个阶段实际发生了重载机制? - Krolique
错误,更正:[2]... 实际的“重载”机制是否发生?=) - Krolique
@Krolique,A在编译时实际上不持有任何东西(它只是一个定义,不是执行的代码)。当您在类B中定义变量s时,重载就会发生。此时,您在两个不同的类中拥有相同名称的两个字段。编译器确定您在代码中要引用哪一个。这不是动态的。 - Yishai
谢谢,我认为这样可以更清楚地解释事情! - Krolique

2
你可能希望像方法一样覆盖字段,根据对象的运行时类型进行动态分派。
这不是Java的工作方式。字段不会被覆盖,它们被隐藏。这意味着类B的对象有两个名为"s"的字段,但访问哪一个取决于上下文。
至于为什么这样做:覆盖字段并没有多大意义,因为在类型不同的情况下没有有用的方法来使其工作,并且当类型相同时也没有意义(因为可以使用超类字段)。个人认为应该只是编译器错误。

谢谢你的回答。我只是想确认我是否正确理解了这个概念。上下文暗示了声明类型吗? 假设可以向上转型并且我执行了以下操作:B b = new A(); 那么在b.s中应该打印“B”吗? - Krolique
既然那不可能实现,争论它应该打印什么就有点毫无意义了,但是没错,那是基本的想法。 - Michael Borgwardt
谢谢,我认为这样可以把事情澄清很多! - Krolique

1

这不是多态(如标记所示)。

Java有虚方法,没有虚成员变量 - 也就是说,你不能覆盖一个属性 - 你只能隐藏它。


1
这不是一个属性,而是一个字段。 - Wim
一个成员变量。术语并不严格。 - Bozho
谢谢您的回答。我错过了虚拟(继承?)方法和成员之间微妙的区别。 - Krolique
阅读我提供的维基百科文章,以了解什么是虚拟方法。 - Bozho

0

尽管成员变量是从基类继承的,但它们不是多态调用(即动态调用不适用于成员变量)。

因此,a.s将引用基类中的成员而不是派生类中的成员。

话虽如此,该代码没有遵循面向对象的原则。类的成员需要是私有/受保护的(根据业务用例而定),并且需要提供公共方法来获取和设置成员的值。


谢谢您的回答。在所有正常情况下,我会按照您建议的做法去做。 - Krolique

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接