为什么这个Java代码会创建堆栈溢出错误?
下面的代码在执行时会产生堆栈溢出错误。但是,如果删除以下任一项:为什么这个Java代码会创建堆栈溢出错误?,java,Java,下面的代码在执行时会产生堆栈溢出错误。但是,如果删除以下任一项: static final GenerateStackOverflow E1=新GenerateStackOverflow(“值1”) final GenerateStackOverflow E2=新GenerateStackOverflow(“值2”) 它运行时没有堆栈溢出错误。如果我有上面两行代码,为什么会出现堆栈溢出错误?如果类中只有一行代码,为什么没有错误 public class GenerateStackOverfl
static final GenerateStackOverflow E1=新GenerateStackOverflow(“值1”)代码>
final GenerateStackOverflow E2=新GenerateStackOverflow(“值2”)代码>
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();
}
}
生成
堆栈溢出错误
时需要这两个选项。当您包括此行时:
static final GenerateStackOverflow E1 = new GenerateStackOverflow("value1");
当类首次被访问时,会创建GenerateStackOverflow
的实例
不包括这一行:
final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
一切都很好。但这一点至关重要。每次创建GenerateStackOverflow
的实例时,它都会尝试初始化其成员变量E2
——另一个GenerateStackOverflow
对象。然后该实例将其E2
初始化为另一个GenerateStackOverflow
对象。这将一直持续到出现堆栈溢出错误
如果第二行包含但第一行不包含,则不会创建任何实例,也不会输入无限递归。静态final行表示每次加载类时都会实例化一个
GenerateStackOverflow
;那只是一次。final
行表示每次实例化类时都会实例化一个类
您的main
方法加载类,但不实例化它。因此:
- 仅使用
行,加载类实例化static final
,就是这样GenerateStackOverflow
- 仅使用
行,加载类不会做任何进一步的工作final
- 在这两种情况下,加载类实例化一个
(由于GenerateStackOverflow
行),然后实例化另一个static
(由于非GenerateStackOverflow
行),然后实例化另一个static
,依此类推,直到得到堆栈溢出GenerateStackOverflow
main
方法改为:
new GenerateStackOverflow("boom");
。。。那么,仅使用非静态行就足以导致溢出。构造函数调用自身:
final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");
所以,要构造一个实例,你需要构造一个实例,它需要构造一个实例,等等
程序的main方法加载该类。还有一个静态字段,它调用类的构造函数,从而创建堆栈溢出。因此,删除静态变量隐藏了问题,因为从未调用构造函数。删除非静态变量会完全删除递归调用,这就解决了问题。递归构造函数调用是简单的答案
戏法
用于变量的
static final
,以便调用构造函数,并且构造函数inturn尝试使用final
变量创建自身实例,从而导致递归调用。这将生成无任何字段的无限循环:
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();
}
}
产生循环的不是最终字段,而是新操作
您还可以通过删除static
子句并插入new GenerateStackOverflow(…)
调用main
来获得错误。您的代码示例在仍然显示类似行为的情况下,可以简化为:
public class Foo {
static Foo T1 = new Foo();
Foo T2 = new Foo();
Foo() {
}
public static void main(String[] args) {
}
}
public class Foo {
static Foo T1 = new Foo();
Foo T2;
Foo() {
T2 = new Foo();
}
public static void main(String[] args) {
}
}
T2将在创建实例时被分配,这意味着代码可以重构为:
public class Foo {
static Foo T1 = new Foo();
Foo T2 = new Foo();
Foo() {
}
public static void main(String[] args) {
}
}
public class Foo {
static Foo T1 = new Foo();
Foo T2;
Foo() {
T2 = new Foo();
}
public static void main(String[] args) {
}
}
第二种形式很明显,构造函数正在调用自己。那么为什么移除T1或T2也会移除StackOverflower错误呢
- 删除带有T1的行后,将运行main方法,但
该类从未被实例化。(调用Foo.class.getName()不会调用构造函数。)
- 删除T2后,构造函数将不再调用自身,StackOverflowerError的源也将被删除
因为对象类的每个实例都是该类的新实例。创建类的新实例,创建类的新实例…您正在创建一个无限长的实例链接列表(通过E2
引用)。对象在实例化时正在实例化它自己…您只会在两行中得到错误,因为没有第一行就不会创建类的实例,没有第二行就不会递归。不确定为什么会收到否决票。有趣的例子。