为什么这个Java代码会产生堆栈溢出错误?

时间:2013-08-30 22:23:53

标签: java

执行时,下面的代码会产生堆栈溢出错误。但是如果删除

中的任何一个
  • 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();
    }
}

6 个答案:

答案 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的源。