实例变量,当在子类中重新初始化时#39;初始化块,像实例方法一样被覆盖,它不应该被覆盖

时间:2015-02-14 16:46:28

标签: java inheritance polymorphism

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

  1. 与实例方法不同,实例变量不能被覆盖,并且在多态访问时,JVM在运行时不会动态选择它。
  2. 执行流程:静态块,超级构造函数,初始化块,然后是构造函数。
  3. 但是我被困在一个代码中,我已经多态地调用了一个实例变量,但它显示了重写值(它不应该显示)。在子类'init block。

    中重写实例变量
    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

    所以我怀疑是:

    怀疑1:为什么它显示“35”,它应该显示超类'变量的值为50。

    怀疑2:当它为最后一个案例显示子类'变量的值时,为什么不为这种情况。

2 个答案:

答案 0 :(得分:2)

  

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

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

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

  

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

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

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

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

  6.   
  7. 为此类执行实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值分配给相应的实例变量,按从而出现的从左到右的顺序在文本的源代码中。如果执行任何这些初始值设定项导致异常,则不会处理其他初始化程序,并且此过程会突然完成同样的异常。否则,请继续步骤5.

  8.   
  9. 执行此构造函数的其余部分。如果执行突然完成,则此过程突然完成,原因相同。否则,此过程正常完成。

  10.   

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

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

答案 1 :(得分:1)

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

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

例如:

class Parent {
   public int age = 50;
}

class Child {
   public int age = 80;
}

我们在这里定义了一个新成员age,该成员与父成员age分开。如果您使用Childthis.age内部访问它,则会获得80。如果您使用age访问父级super.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

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

简单地在从父级继承的字段中指定值不会覆盖。它是继承的一部分。