为什么这个语句不抛出StackOverflowError?

2022-08-31 13:18:18

我刚刚在另一个问题中看到了这段奇怪的代码。我以为这会导致被抛出,但事实并非如此......StackOverflowError

public class Node {
    private Object one;
    private Object two;
    public static Node NIL = new Node(Node.NIL, Node.NIL);

    public Node(Object one, Object two) {
        this.one = one;
        this.two = two;
    }
}

我以为它会是一个例外,因为引用本身是要构建的。Node.NIL

我不明白为什么它没有。


答案 1

NIL是一个静态变量。当类初始化时,它会初始化一次。初始化时,将创建单个实例。它的创建不会触发任何其他实例的创建,因此没有无限的调用链。传递给构造函数调用与传递具有相同的效果,因为当调用构造函数时,它尚未初始化。因此 与 相同。NodeNodeNodeNode.NILnullNode.NILpublic static Node NIL = new Node(Node.NIL, Node.NIL);public static Node NIL = new Node(null, null);

另一方面,如果是一个实例变量(并且没有作为参数传递给构造函数,因为在这种情况下编译器会阻止您将其传递给构造函数),则每次创建实例时都会初始化它,这将创建一个新实例,其创建将初始化另一个实例变量, 导致无限的构造函数调用链,这些调用将以 结尾。NILNodeNodeNodeNILStackOverflowError


答案 2

变量 NIL 首先给出值,然后从上到下初始化一次。它不是一个函数,也不是以递归方式定义的。在初始化之前使用的任何静态字段都具有默认值,并且您的代码与null

public static Node {
    public static Node NIL;

    static {
        NIL = new Node(null /*Node.NIL*/, null /*Node.NIL*/);
    }

    public Node(Object one, Object two) {
        // Assign values to fields
    }
}

这与写作没有什么不同

NIL = null; // set implicitly
NIL = new Node(NIL, NIL);

如果你定义了一个像这样的函数方法,你会得到一个StackoverflowException。

Node NIL(Node a, Node b) {
    return NIL(NIL(a, b), NIL(a, b));
}

推荐