执行时,下面的代码会产生堆栈溢出错误。但是如果删除
中的任何一个static final GenerateStackOverflow E1 = new GenerateStackOverflow("value1");
final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
它在没有堆栈溢出错误的情况下运行。如果我有上面的两行,怎么会出现堆栈溢出错误,但如果只有一行是在类中,则没有错误?
public class GenerateStackOverflow {
private final String value;
static final GenerateStackOverflow E1 = new GenerateStackOverflow("value1");
final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
public GenerateStackOverflow(String value) {
System.out.println("GenerateStackOverflow.GenerateStackOverflow()");
this.value = value;
}
public String getValue() {
return value;
}
public static void main(String[] args) {
GenerateStackOverflow.class.getName();
}
}
答案 0 :(得分:5)
两者都需要生成StackOverflowError
。当您包含此行时:
static final GenerateStackOverflow E1 = new GenerateStackOverflow("value1");
如果首次访问该类,则会创建GenerateStackOverflow
的实例。
不包括这一行:
final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
事情很好。但这条线很重要。每次创建GenerateStackOverflow
实例时,它都会尝试初始化其成员变量E2
- 另一个GenerateStackOverflow
对象。然后该实例将其 E2
初始化为另一个GenerateStackOverflow
对象。这一直持续到StackOverflowError
发生。
如果包含第二行但第一行不包含,则不会创建任何实例,并且永远不会输入无限递归。
答案 1 :(得分:5)
构造函数调用自身:
final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
因此,要构造一个实例,需要构造一个需要构造实例的实例,等等。
你的程序的main方法加载了这个类。并且有一个静态字段调用类的构造函数,这会创建堆栈溢出。因此,删除静态变量会隐藏问题,因为永远不会调用构造函数。删除非静态变量会完全删除递归调用,从而修复了probem。
答案 2 :(得分:1)
static final
行表示每次加载类时都会实例化GenerateStackOverflow
;这只是一次。 final
行意味着每次实例化类时都会实例化一个。
您的main
方法加载了类,但没有实例化它。所以:
static final
行,加载类会实例化GenerateStackOverflow
,就是final
行,加载课程不会做任何进一步的操作GenerateStackOverflow
(由于static
行),然后实例化另一个GenerateStackOverflow
(由于非static
行) ,然后实例化另一个 GenerateStackOverflow
,依此类推,直到你得到堆栈溢出。如果您的main
方法改为:
new GenerateStackOverflow("boom");
...然后只有非static
行才足以导致溢出。
答案 3 :(得分:0)
递归构造函数调用是一个简单的答案。
将static final
用于变量,以便调用构造函数并且构造函数inturn尝试使用final
变量创建自身实例,从而导致递归调用。
答案 4 :(得分:0)
这会产生无任何字段的无限循环:
public class GenerateStackOverflow {
private final String value;
static {
GenerateStackOverflow E1 = new GenerateStackOverflow("value1");
}
public GenerateStackOverflow(String value) {
System.out.println("GenerateStackOverflow.GenerateStackOverflow()");
GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
this.value = value;
}
public String getValue() {
return value;
}
public static void main(String[] args) {
GenerateStackOverflow.class.getName();
}
}
这不是产生循环的最终字段,而是new
操作。
您还可以通过删除static
子句并在new GenerateStackOverflow(...)
中插入main
来获取错误。
答案 5 :(得分:0)
您的代码示例虽然仍然显示类似的行为,但可以简化为:
public class Foo {
static Foo T1 = new Foo();
Foo T2 = new Foo();
Foo() {
}
public static void main(String[] args) {
}
}
在创建实例时会分配T2,这意味着代码可以重构为:
public class Foo {
static Foo T1 = new Foo();
Foo T2;
Foo() {
T2 = new Foo();
}
public static void main(String[] args) {
}
}
这第二种形式很明显构造函数正在调用它自己。那么为什么删除T1或T2也会删除StackOverflowError?
当删除带有T1的行时,main方法将运行但是 该类永远不会被实例化。 (调用Foo.class.getName()不会调用构造函数。)
当删除T2时,构造函数将不再调用自身,并删除StackOverflowError的源。