在Java中重写成员variables

我正在学习在JAVA中覆盖成员函数,并考虑重写成员variables的实验。

所以,我定义了类

public class A{ public int intVal = 1; public void identifyClass() { System.out.println("I am class A"); } } public class B extends A { public int intVal = 2; public void identifyClass() { System.out.println("I am class B"); } } public class mainClass { public static void main(String [] args) { A a = new A(); B b = new B(); A aRef; aRef = a; System.out.println(aRef.intVal); aRef.identifyClass(); aRef = b; System.out.println(aRef.intVal); aRef.identifyClass(); } } 

输出是:

 1 I am class A 1 I am class B 

我不明白为什么当aRef设置为b intVal仍然是类A?

当你在一个子类中创build一个相同名字的variables时,这就是所谓的隐藏 。 现在生成的子类实际上具有两个属性。 你可以使用super.var((SuperClass)this).var来访问超类。 variables甚至不必是相同的types; 他们只是两个variables共享一个名字,就像两个重载的方法。

Java中的variables不是多态的; 他们不会互相推翻。

variablesparsing编译时,方法运行时。 aRef属于Atypes,因此aRef.Intvalue编译时parsing为1。

从JLS Java SE 7版本15.11.1开始:

这种缺乏对字段访问的dynamic查询允许程序以直接实现的方式高效运行。 后期绑定和覆盖的function是可用的,但只有在使用实例方法时才有效。

奥利弗·查尔斯沃思(Oliver Charlesworth)和马可·托波利尼克(Marko Topolnik)的答案是正确的,我想详细解释一下为什么问题的一部分:

在Java 类中,成员是根据引用的types来访问的 ,而不是实际对象的types。 出于同样的原因,如果在类B有一个someOtherMethodInB() ,那么在运行aRef = b之后,将无法从aRef访问它。 标识符(即类,variables等名称)在编译时parsing,因此编译器依赖于引用types来执行此操作。

现在在你的例子中,运行System.out.println(aRef.intVal); 它将打印A定义的intVal的值,因为这是用来访问它的引用的types。 编译器看到aRefAtypesA ,它将访问它的aRef 。 不要忘记,在B的情况下你有两个字段。 JLS也有一个类似于你的例子,“15.11.1-1。静态绑定的字段访问”,如果你想看看。

但为什么方法的行为有所不同呢? 答案是对于方法,Java使用后期绑定 。 这意味着在编译时,它find了在运行时search的最合适的方法。 search涉及在某个类中被覆盖的方法的情况。

Java中的字段没有多态性。

Variables决定发生在编译时,所以总是会访问基类variables (而不是子的inheritancevariables)。

所以每当上传时总会记得

1)基类variables将被访问。

2)子类方法(覆盖的方法,如果重写发生,否则inheritance的方法,因为它是从父母)将被调用。

那么,我希望你能得到答案。 如果没有,你可以尝试在debugging模式下看到。 子类B有权访问两个intVal。 它们不是多态的,因此它们没有被覆盖。

如果你使用B的参考,你会得到B的intVal。 如果你使用A的引用,你会得到A的intVal。 就这么简单。

根据Java规范,实例variables在扩展时不会被子类从超类覆盖。

因此,子类中的variables只能被看作共享相同名称的variables。

另外,当在B的实例创build期间调用A的构造函数时,variables(intVal)被初始化并因此被输出。

Java具有封装的特性,意味着它紧密地绑定一个对象的属性和行为。 所以只能通过一个类的引用,我们可以称之为行为来改变它的属性。

而在inheritance中,只有方法重写才能影响它的属性。