簡體   English   中英

為什么這個Java代碼會產生堆棧溢出錯誤?

[英]Why does this Java code create a stack overflow error?

執行時,下面的代碼會產生堆棧溢出錯誤。 但是,如果刪除其中之一

  • 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();
    }
}

兩者都需要生成StackOverflowError 當您包含此行時:

static final GenerateStackOverflow E1 = new GenerateStackOverflow("value1");

如果首次訪問該類,則會創建GenerateStackOverflow的實例。

沒有這一行包括:

final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");

事情很好。 但這條線很重要。 每次創建GenerateStackOverflow實例時,它都會嘗試初始化其成員變量E2 - 另一個GenerateStackOverflow對象。 然后實例將 E2初始化為另一個GenerateStackOverflow對象。 這將一直持續到發生StackOverflowError

如果包含第二行但第一行不包含,則不會創建任何實例,並且永遠不會輸入無限遞歸。

構造函數調用自身:

final GenerateStackOverflow E2 = new GenerateStackOverflow("value2");

因此,要構造一個實例,需要構造一個需要構造實例的實例,等等。

你的程序的main方法加載了這個類。 並且有一個靜態字段調用類的構造函數,這會創建堆棧溢出。 因此,刪除靜態變量會隱藏問題,因為永遠不會調用構造函數。 刪除非靜態變量會完全刪除遞歸調用,從而修復了probem。

static final行意味着每次加載類時都會實例化GenerateStackOverflow ; 這只是一次。 final一行表示每次實例化類時都會實例化一行。

您的main方法加載類但不實例化它。 所以:

  • 僅使用static final行,加載類將實例化GenerateStackOverflow ,就是這樣
  • 僅使用final一行,加載類不會做任何進一步的操作
  • 使用兩者,加載類實例化GenerateStackOverflow (由於static行),然后實例化另一個GenerateStackOverflow (由於非static行),然后實例化另一個 GenerateStackOverflow ,依此類推,直到你得到堆棧溢出。

如果你的main方法是:

new GenerateStackOverflow("boom");

...然后只是非static線就足以引起溢出。

遞歸構造函數調用是一個簡單的答案。

特技

對於變量使用static final ,以便調用構造函數並且構造函數inturn嘗試使用final變量創建自身的實例,這將導致Recursive調用。

這會產生無任何字段的無限循環:

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子句並在main插入new GenerateStackOverflow(...)調用來獲取錯誤。

您的代碼示例雖然仍然顯示類似的行為,但可以簡化為:

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的源代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM