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

时间:2017-01-28 15:06:55

标签: java inheritance constructor initialization field

当我发现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. 父字段初始化,但是childField已经存在且具有默认值(childField = null)
  2. 父构造函数
    • 由父构造函数调用的重写的init()(childField ="已初始化")
  3. 子字段初始化:childField = null(再次)
  4. 子构造函数
  5. 我知道这个例子充满了不良做法。但是,我的直观顺序是:字段初始化(从父项到子项),然后是构造函数(从父项到子项)。

    这种初始化顺序的目的是什么? 为什么字段初始值设定项在首次使用之前未执行? 如果该字段尚未初始化,那么为什么允许使用它呢?

1 个答案:

答案 0 :(得分:2)

让我解释一下构建新对象的“直观”顺序:

  1. 父母的字段已初始化
  2. 初始化儿童字段
  3. 父的构造函数被称为
  4. 调用Child的构造函数
  5. 嗯,这不太合理,因为子字段的初始化可能依赖于父级。

    当构造函数返回时,可以将对象视为“正确初始化”。同意?

    不要使用ParentChild,而是使用BoxTreasureBox。要构建TreasureBox,首先要制作Box。创建框后,您可以为其添加不同的装饰,使其看起来非常华丽和酷炫,您还可以添加锁或其他内容。

    请参阅?这里的订单?在初始化子代之前首先正确初始化父代表是最有意义的,这意味着子类的任何初始化必须在父代的构造函数返回之后发生。这正是Java正在做的事情。

    孩子的字段可以取决于父母的字段。要锁定TreasureBox,您需要找到方框的正面,并将其放在那里。如果尚未创建盒子的正面,你怎么能锁定它?

    这里有一些代码来澄清我的意思:

    class Parent {
    
        public String parentField;
    
        public Parent(){
            parentField = "Hello";
        }
    }
    
    class Child extends Parent {
    
        public int childField = parentField.length();
    }
    

    如果Java使用您的“直观”订单,则会抛出NPE。