从非静态上下文调用相同类的 Java 构造函数会导致递归,但使用静态它工作正常吗?



我正在尝试了解Java类的初始化顺序。 具体来说,静态的和实例初始值设定项/字段的执行时间和顺序。我想出了这个堆栈溢出问题中的示例。为什么向自构造函数调用添加静态会阻止代码进入递归状态。

public class Test {
public static void main(String a[]) {
Cons1 c1 = new Cons1();
}
}
class Cons1 {
static Cons1 c = new Cons1(); /* if static is removed then recursion 
occurs */
Cons1() {
//does something
}
}

静态上下文和实例上下文之间的行为差异是否有任何特定原因。我浏览了 Java 文档详细初始化过程,但无法理解这种行为背后的逻辑是什么。任何对JLS规范的解释或参考都将有所帮助。

PS :我已经浏览了这个类似的堆栈溢出帖子,但是我无法从那里得到答案。

如果一个字段被声明为静态,则无论最终可能创建该类的多少个实例(可能为零个),该字段都只存在一个化身。静态字段(有时称为类变量)在初始化类时化身 (§12.4)。

JLS 10 - 8.3.1.1. 静态字段

另一方面

如果声明符用于实例变量(即非静态字段),则以下规则适用于其初始值设定项:

  • 在运行时,每次创建类的实例时,都会计算初始值设定项并执行赋值。

JLS 10 - 8.3.2.字段初始化


让我们添加一个println语句来查看整个画面:

class Cons1 {
static Cons1 c = new Cons1();
Cons1() {
System.out.println("the constructor was called");
}
public static void main(String[] args) {
Cons1 c1 = new Cons1();
Cons1 c2 = new Cons1();
}
}

它输出"构造函数被调用"三次:

1 - 加载类并初始化静态字段c时;

2 - 创建c1时;
3 - 创建c2时。

现在,我们将它与带有实例字段的示例进行比较:

class Cons1 {
Cons1 c = new Cons1();
Cons1() {
System.out.println("the constructor was called");
}
public static void main(String[] args) {
Cons1 c1 = new Cons1();
}
}

显然,它在下一个堆栈跟踪不打印任何内容时失败:

Exception in thread "main" java.lang.StackOverflowError
at Cons1.<init>(Cons1.java:33)
...
at Cons1.<init>(Cons1.java:33)

原因是Cons1的每个实例都需要另一个Cons1对象。因此,我们溢出了调用堆栈,在其中推送Cons1.<init>方法。因此,当堆栈达到其最大允许大小时,我们最终会出现异常。

最新更新