实例变量,当在 sub class' 初始化块中重新初始化时,像实例方法一样被覆盖,它不应该

Instance variable, when re-initialised in sub class' initialization block, is overridden like instance method, which it should not

刚开始学习Java.I明白

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

但我被困在一个代码中,我在其中以多态方式调用了一个实例变量,但它显示了被覆盖的值(它不应该显示)。实例变量在 sub class' 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

所以我的疑惑是:

疑问1:为什么显示“35”,应该显示super class'变量的值是50.

疑问2:当它显示子class'变量的最后一个案例的值时,为什么不显示这个案例。

Why is it showing "35", it should display super class' variable's value which is 50.

subclass Child 的初始化块执行 变量 ageParent class。因此,age先初始化为50,再初始化为35.

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

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:

  1. Assign the arguments for the constructor to newly created parameter variables for this constructor invocation.

  2. If this constructor begins with an explicit constructor invocation (§8.8.7.1) of another constructor in the same class (using this), then evaluate the arguments and process that constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason; otherwise, continue with step 5.

  3. This constructor does not begin with an explicit constructor invocation of another constructor in the same class (using this). If this constructor is for a class other than Object, then this constructor will begin with an explicit or implicit invocation of a superclass constructor (using super). Evaluate the arguments and process that superclass constructor invocation recursively using these same five steps. If that constructor invocation completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, continue with step 4.

  4. Execute the instance initializers and instance variable initializers for this class, assigning the values of instance variable initializers to the corresponding instance variables, in the left-to-right order in which they appear textually in the source code for the class. If execution of any of these initializers results in an exception, then no further initializers are processed and this procedure completes abruptly with that same exception. Otherwise, continue with step 5.

  5. Execute the rest of the body of this constructor. If that execution completes abruptly, then this procedure completes abruptly for the same reason. Otherwise, this procedure completes normally.

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

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

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

覆盖正在创建一个新成员,它是当前 class 的一部分,它与 parent class 的成员具有相同的定义,并且该成员是当您多态地使用 object 时访问。

例如:

class Parent {
   public int age = 50;
}

class Child {
   public int age = 80;
}

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

但这不是压倒一切的,因为如果您多态地使用 object,它将访问 parent 的 age:

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

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

尽管它们是相同的 object。那是因为 child 隐藏了 字段而不是 覆盖 它。

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