为什么这个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
引用)。对象在实例化时正在实例化它自己…您只会在两行中得到错误,因为没有第一行就不会创建类的实例,没有第二行就不会递归。不确定为什么会收到否决票。有趣的例子。