实例变量何时初始化并分配值?输出:

实例变量何时初始化?是在构造函数块完成之后还是在它之前?

请考虑以下示例:

public abstract class Parent {

 public Parent(){
   System.out.println("Parent Constructor");
   init();
 }

 public void init(){
   System.out.println("parent Init()");
 }
}

public class Child extends Parent {

private Integer attribute1;
private Integer attribute2 = null;

public Child(){
    super();
    System.out.println("Child Constructor");
}

public void init(){
    System.out.println("Child init()");
    super.init();
    attribute1 = new Integer(100);
    attribute2 = new Integer(200);
}

public void print(){
    System.out.println("attribute 1 : " +attribute1);
    System.out.println("attribute 2 : " +attribute2);
}
}

public class Tester {

public static void main(String[] args) {
    Parent c = new Child();
    ((Child)c).print();
    
}
}

输出:

父构造函数

子初始化()

父初始化()

子构造函数

属性 1 : 100

属性 2:空


  1. 何时在堆中分配属性 1 和 2 的内存?

  2. 想知道为什么属性 2 是 NULL?

  3. 是否有任何设计缺陷?


答案 1

当在堆中分配 atribute 1 和 2 的内存时?

整个对象的内存是在调用运算符时分配的,在输入构造函数之前。为 中的单个实例分配内存是没有意义的,但是当内存分配给单个属性时是没有意义的 - 只有整个对象。newjava.lang.ObjectIntegerinit

想知道为什么属性 2 是 NULL?

该方法在超构造函数中被调用,因此被赋值,然后调用子类构造函数,该子类构造函数按照它们在源代码中出现的顺序应用属性初始值设定项。此行initattribute2new Integer(200)

private Integer attribute2 = null;

覆盖 分配给 的值。init()null

如果将呼叫添加到

 System.out.println("attribute 2 : " +attribute2);

在您打电话之后,这将变得明显。super();

是否有任何设计缺陷?

在基类完成初始化之前调用子类方法是危险的。子类可能依赖于其基类的不变量来保护其自身的不变量,如果基类构造函数尚未完成,则其不变量可能不成立。

这也可能会混淆C++程序员等,他们希望从基类调用基类来调用基类的版本,因为C++在输入构造函数时会重写 vtable 指针。init

请参阅 Java 语言规范,了解所有血腥的细节。


答案 2

在消费了这里提供的答案和链接之后,这是我的摘要观察:


流程如下:

  1. 输入子类构造函数。Child(){ ... }

  2. 调用显式 super() [调用父类构造函数]。

  3. 输入 Parent() { ... } 类构造函数

  4. 调用隐式 super() [调用对象类构造函数]

  5. Enter Object(){ } (无超构造函数调用)

  6. 对超类构造函数的递归调用在此处结束。

  7. 对象类构造函数的返回值

  8. 现在在父类构造函数中...执行父类的实例初始值设定项和实例变量初始值设定项。

  9. 父类构造函数的其余部分被执行并返回

  10. 现在在子类构造函数中。执行子类的实例初始值设定项和实例变量初始值设定项。

  11. 然后执行子类构造函数的其余部分并完成对象初始化过程。


原因属性 2 为 NULL,因为

  1. 在步骤 9 中为 attribute2 分配一个值 200。
  2. 但在步骤 10 中重写为 NULL

是否有任何设计缺陷?

正如Fabian Barney所提到的:::::在构造函数内调用可以被子类覆盖的方法通常是不好的做法。

当在堆中分配 atribute 1 和 2 的内存时?仍在弄清楚。感谢任何指点。

感谢迈克和法比安