为什么这些字段在可能首次使用之前没有初始化?

Why are not the fields initialized before their possible first use?

当我发现 Java 中的字段初始化有一些奇怪的顺序时,我感到非常困惑。 init() 结果被字段初始化覆盖时的示例代码:

public abstract class Parent {

    public String parentField = "dupa";

    public Parent(){
        init(); // uhh, bad practice to call abstract method in a super constructor
    }

    protected abstract void init();
}

public class Child extends Parent {

    public String childField = null; // assigning null is unnecessary, another bad practice

    @Override
    protected void init(){
        childField = "initialized";
        System.out.println("After init(): " + childField);
    }
}

...

Child child = new Child(); // OUTPUT: After init(): initialized
System.out.println("After all: " + child.childField); // OUTPUT: After all: null

我发现调用时的执行顺序是什么 new Child();:

  1. Parent 字段初始化,但 childField 已经存在并具有默认值 (childField = null)
  2. Parent构造函数
    • 被 parent 构造函数调用的重写 init() (childField = "initialized")
  3. Child 字段初始化:childField = null(再次)
  4. Child构造函数

我知道这个例子充满了不好的做法。然而,对我来说直观的顺序是:字段初始化(从 parents 到 childs),然后是构造函数(从 parents 到 childs)。

这样的初始化顺序有什么用? 为什么字段初始值设定项在首次使用之前不执行? 如果该字段还没有初始化,为什么允许使用它?

让我解释一下你的 "intuitive" 构建新 object 的顺序:

  1. Parent 的字段已初始化
  2. Child 的字段已初始化
  3. Parent的构造函数被调用
  4. Child的构造函数被调用

嗯,这不太合理,因为 child 字段的初始化可能依赖于 parent。

一个object的构造函数returns可以认为是"properly initialized"。同意吗?

我们不使用 ParentChild,而是使用 BoxTreasureBox。要构建 TreasureBox,您首先要构建 Box。盒子做好后,你可以给它加上不同的装饰,让它看起来很酷很酷,你也可以加一把锁什么的。

看到了吗?这里的顺序?在初始化 child 之前首先正确初始化 parent 是最有意义的,这意味着 child class 的任何初始化必须在 parent 之后进行的构造函数 returns。这正是 Java 正在做的事情。

child 的字段可以依赖于 parent 的字段。要给 TreasureBox 上锁,你需要找到盒子的正面,然后把它放在那里。如果盒子的正面还没有创建,你怎么给它加锁?

这里有一些代码可以阐明我的意思:

class Parent {

    public String parentField;

    public Parent(){
        parentField = "Hello";
    }
}

class Child extends Parent {

    public int childField = parentField.length();
}

如果Java使用您的"intuitive"订单,将抛出NPE。