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

时间:2012-05-14 19:28:18

标签: java inheritance initialization

我知道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 个答案:

答案 0 :(得分:15)

没错。 super(),即使你没有明确地添加它,也会被隐含地放在每个构造函数中。这意味着首先调用ShufflePieceSequence的构造函数,但它所做的就是调用AbstractPieceSequence

AbstractPieceSequence中,您正在调用ShufflePieceSequence中定义的方法 - 该方法尚未初始化。事实上,你所做的实际上是一个非常微妙的bug 。你永远不应该从构造函数调用overridable(包括abstract方法)。期。像这样的AFAIR工具将此标记为潜在错误。

另见

答案 1 :(得分:4)

对象字段未被隐式初始化...您需要执行init。也许在这种情况下你需要一个懒惰的初始化?让构造函数调用方法做一些非常重要的工作通常很不愉快,通常是一种比想要的更复杂的气味。

答案 2 :(得分:3)

深度优先,预订步行。

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

答案 3 :(得分:0)

在继承的情况下调用Parent-Sub类的构造函数的顺序是,Parent类的构造函数总是首先调用,然后是Child类的构造函数。

如果没有给出,Sub类默认使用Super()调用基类的构造函数。