Java构造函数与字段初始化顺序

9
我知道Java对象构造函数会隐式地初始化其实例的非静态字段。但是,我不确定在类层次结构中这发生的顺序。例如:
abstract public class AbstractPieceSequence implements PieceSequence
{
    private Tetromino current;
    private Tetromino preview;

    public AbstractPieceSequence()
    {
        advance();
    }

    @Override
    public final void advance()
    {
        if (preview == null) {
            current = getNextPiece();
            preview = getNextPiece();
        } else {
            current = preview;
            preview = getNextPiece();
        }
    }

    abstract protected Tetromino getNextPiece();
}

public class ShufflePieceSequence extends AbstractPieceSequence
{
    private List<Shape> bag = new LinkedList<Shape>();

    @Override
    protected Tetromino getNextPiece()
    {
        if (bag.size() == 0) {
            Collections.addAll(bag, Shape.I, Shape.J, Shape.L, Shape.O, Shape.S, Shape.T, Shape.Z);
        }

        return Tetromino.tetrominoes.get(bag.remove(0));
    }
}

父类的构造函数调用了子类中的一个方法,但由于 List<Shape> bag 的值目前为 null,该方法会抛出异常。

我可以定义一个子类构造函数并调用 super(),但这必须是构造函数体中的第一行(这意味着在调用 getNextPiece 之前我仍然没有机会初始化 bag)。

我似乎漏掉了某些明显的东西。

4个回答

15
没错。即使你不显式添加它,super()也会在每个构造函数中隐式放置。ShufflePieceSequence的构造函数会首先被调用,但它做的第一件事情就是调用AbstractPieceSequence
AbstractPieceSequence中,您调用了一个在ShufflePieceSequence中定义但尚未初始化的方法。实际上,你所做的是一个非常微妙的错误。您永远不应该从构造函数中调用可重写(包括abstract方法)。时期。像这样的工具将其标记为潜在错误。
另请参见:

4

对象字段不会自动初始化...您需要进行初始化。也许在这种情况下您需要一种延迟初始化方式?通常情况下,构造函数调用执行非平凡工作的方法是不好的,这通常意味着某些东西比它想要的更加复杂。


3

深度优先,先序遍历。

Anders提出了一个很好的观点:Java仅隐式地初始化本机类型字段。任何对象字段仅是对象的引用,因此实际上已经被初始化,但是它被初始化为null


0
在继承的情况下,父类和子类构造函数的调用顺序是先调用父类构造函数,然后再调用子类构造函数。
如果没有显式给出,子类默认使用Super()来调用基类的构造函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接