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

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

考虑这个例子:

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(); } } 

OUTPUT:

父构造函数

子init()

父Init()

儿童构造函数

属性1:100

属性2:null


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

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

  3. 有任何设计缺陷吗?

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

在输入java.lang.Object构造函数之前,在调用new运算符时分配整个对象的内存。 在init为各个Integer实例分配内存,但是为单个属性分配内存没有意义 – 只有整个对象。

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

在超级构造函数中调用init方法,因此为attribute2分配了new Integer(200) ,然后调用子类构造函数,它按照它们在源代码中出现的顺序应用属性初始值设定项。 这条线

 private Integer attribute2 = null; 

init()指定的值覆盖为null

如果你添加一个电话

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

在你打电话给super(); 那么这将变得明显。

有任何设计缺陷吗?

在基类完成初始化之前调用子类方法是危险的。 子类可能依赖于其基类的不变量来保护自己的不变量,如果基类构造函数没有完成,那么它的不变量可能不会成立。

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

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


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


这是流程:

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

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

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

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

  5. 输入Object(){}(没有超级构造函数调用)

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

  7. Object类构造函数的返回值

  8. 现在在Parent类构造函数中……实现了Parent类的实例初始值设定项和实例变量初始值设定项。

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

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

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


attribute2的原因是NULL,因为

  1. attribute2被赋值为200 @ step 9。
  2. 但在步骤10中重写为NULL

有任何设计缺陷吗?

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

当在堆中分配属性1和2的内存时? 还在搞清楚。 感谢任何指针。

感谢Mike和Fabian