【问题标题】:Instance variable, when re-initialised in sub class' initialization block, is overridden like instance method, which it should not实例变量,当在子类的初始化块中重新初始化时,像实例方法一样被覆盖,它不应该
【发布时间】:2015-02-14 16:46:28
【问题描述】:

我刚开始学习Java,我明白了

  1. 与实例方法不同,实例变量不能被覆盖,并且在多态访问时不会被 JVM 在运行时动态选取。
  2. 执行流程:静态块、超级构造函数、初始化块和构造函数。

但我被困在一个代码中,我在其中多态地调用了一个实例变量,但它显示了被覆盖的值(它不应该显示)。实例变量在子类的 init 块中被覆盖。

package package1;

public class Other {
    
    public static void main(String [] args){
            Parent referToChild = new Child();
            Parent referToChildTwo = new ChildTwo();
            System.out.println("age as referred by referToChild reference variable is:" + referToChild.age);// prints 35 (doubt 1)
            System.out.println("age as referred by referToChildTwo reference variable is:" + referToChildTwo.age);// prints 50 (doubt 2)
            System.out.println("money as referred by Other reference variable is:" + referToChild.money);
            //System.out.println("Other reference variable is:" + othObj.age);
    }
}

class Child extends Parent{
    // init block 
    {
        age = 35;
    }
}

class ChildTwo extends Parent{
    public int age;
    {
        age = 40;
    }
}

class Parent{
     public int age = 50;
     public int money = 100;
}

我得到的答案是:

35

50

100

所以我的疑问是:

疑问一:为什么显示“35”,应该显示超类变量的值是50。

疑点2:当它显示子类变量的最后一种情况时,为什么不显示这种情况。

【问题讨论】:

标签: java inheritance polymorphism


【解决方案1】:

为什么显示“35”,应该显示超类变量的值是50。

子类Child的初始化块执行变量ageParent类中初始化。因此,age首先初始化为50,然后初始化为35

Java 语言规范Section 12.5 on Creation of New Class Instances 中对此进行了非常详细的解释,相关部分以粗体显示:

就在对新创建对象的引用作为结果返回之前,使用以下过程处理指示的构造函数以初始化新对象:

  1. 将构造函数的参数分配给此构造函数调用的新创建的参数变量。

  2. 如果此构造函数以对同一类中的另一个构造函数的显式构造函数调用(第 8.8.7.1 节)开始(使用 this),则评估参数并使用这五个相同的步骤递归地处理该构造函数调用。如果该构造函数调用突然完成,则此过程出于相同原因而突然完成;否则,继续第 5 步。

  3. 此构造函数不是以显式调用同一类中的另一个构造函数开始的(使用this)。 如果此构造函数用于 Object 以外的类,则此构造函数将以显式或隐式调用超类构造函数开始(使用 super)。使用这五个相同的步骤以递归方式评估超类构造函数调用的参数和过程。如果该构造函数调用突然完成,则此过程出于相同的原因突然完成。否则,继续第 4 步。

  4. 为这个类执行实例初始化器和实例变量初始化器,将实例变量初始化器的值分配给相应的实例变量,按照它们在文本中出现的从左到右的顺序类的源代码。如果执行这些初始化程序中的任何一个导致异常,则不会处理更多初始化程序,并且此过程会突然完成相同的异常。否则,继续第 5 步。

  5. 执行此构造函数的其余部分。如果该执行突然完成,则此过程出于同样的原因突然完成。否则,此过程正常完成。

关于第二个问题,referToChildTwo 声明为Parent 类型,而其实际类型为ChildTwo。一个ChildTwo 实例有两个age 字段,一个在ChildTwo 中定义,另一个从Parent 继承。

当您编写表达式referToChildTwo.age 时,将评估在Parent 中继承的字段。要评估子类中定义的变量,您需要转换变量,即((ChildTwo)referToChildTwo).age

【讨论】:

  • 谢谢#Mauouti,它很有帮助。 :)
【解决方案2】:

它不能被覆盖,但只要它在父类中不是私有的,它就会被子类继承。他们可以访问它,包括读取和写入。

重写是创建一个新成员,它是当前类的一部分,它与父类的成员具有相同的定义,并且当您以多态方式使用对象时可以访问该成员。

例如:

class Parent {
   public int age = 50;
}

class Child {
   public int age = 80;
}

这里我们定义了一个新成员age,它与父级的age 是分开的。如果您使用this.ageChild 内部访问它,您将获得80。如果您使用super.age 访问父母的age,您将获得50

但这并不是覆盖,因为如果你使用多态对象,它会访问父级的age

Child childObj = new Child();
Parent parentObj = childObj;

System.out.println( childObj.age ); // Will print 80
System.out.println( parentObj.age ); // Will print 50

尽管事实上它们都是同一个对象。这是因为孩子隐藏该字段而不是覆盖它。

在从父级继承的字段中简单地分配一个值并不是覆盖。这是继承的一部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-14
    • 2013-04-14
    • 1970-01-01
    • 2013-07-04
    • 2011-06-21
    • 2017-10-23
    • 2010-10-06
    • 2023-03-14
    相关资源
    最近更新 更多