2015-01-04 49 views
-4

我最近遇到過這種類型的代碼。爲什麼這個Singleton模式不會導致StackOverflowError?

package com.singleton; 

public class Singleton { 
    private static Singleton singleton = new Singleton(); 

    private Singleton() { 
    } 

    public static Singleton getInstance() { 

     return singleton; 
    } 

    public static void main(String[] args) { 

     Singleton obj = Singleton.getInstance(); 
     System.out.println("Done creating Singleton"); 
    } 
} 

現在,這個問題可能乍看起來並不明顯。 ATLEAST不給我:對
因此,添加此功能使不夠清晰

public void print() { 
     System.out.println("Printing inside Singleton Variable"); 

     singleton.print(); 
    } 

    public static void main(String[] args) { 

     Singleton obj = Singleton.getInstance(); 
     obj.print(); 
     System.out.println("Done creating Singleton"); 
    } 

現在的問題,正在運行的程序會導致成StackOVerflowError
我的問題是,目前已是另一種這裏面一個對象代碼中的模式。所以爲什麼它在第一種情況下沒有導致StackOverflowError(即在添加打印功能並在主類中調用它之前)。

+6

在代碼第二部分中,你遞歸調用'print'方法。你還期望什麼?在代碼的第一部分中,您不會遞歸地調用任何方法。 –

+0

爲什麼你認爲第一個代碼片段會導致堆棧溢出? –

+0

僅僅因爲你從'的getInstance返回'singleton'()'不會造成任何遞歸調用。如果你會返回'singleton.GetInstance()',那麼你也會有溢出。 –

回答

2

你的困惑之源是你認爲單例定義是遞歸的。事實上並非如此。

private static Singleton singleton = new Singleton(); 

這是一個靜態字段的定義。這意味着該字段不存在於Singleton的任何實例中。它與Singleton類關聯,並在加載類時進行初始化。

如果這不是一個static電話,那麼你會是對的。創建一個實例會創建一個新的字段,它將創建一個新的實例來創建一個新的字段。

但是對於一個靜態字段,初始化只能在加載類時進行一次。然後調用類的構造函數,這就是它 - 的Singleton沒有更多的作品,沒有更多的初始化,並且沒有自我參照。

所以這個例子會導致StackOverflowError

public class Test 
{ 
    public Test test = new Test(); 

    public Test() { 
    } 

    public static void main (String[] args) { 
     System.out.println(new Test()); 
    } 

} 

,這是因爲該領域test不是static,但一個實例變量,從而創建一個新的實例時,它的初始化完成,並在自身創建一個新的實例等等。

test清晰度改變靜態和錯誤會自行消失。

0

這裏有兩種不同的情況。當Singleton類本身被加載時,Singleton的singleton將被初始化一次。因此,當你在單例中調用getInstance時,它不會重新初始化它自己,而是會一次又一次地返回同一個實例。

你的第二個例子是一個遞歸調用,它調用自身而沒有任何退出條件,因此導致StackOverflowError。

0

您還可能混淆的目的關係,即是遞歸:

class Foo { 
    private Foo foo; 
    Foo(){ ... } 
    public void setFoo(Foo foo){ 
     this.foo = foo; 
    } 
} 

用的方法的動態遞歸。

如果它們不運行到其它類型的遞歸構建有與像美孚類沒有問題,例如,

Foo(){ 
    this.foo = new Foo(); // Ooops! 
} 
+0

值得注意的是,在這種情況下,該領域是靜態的,所以它甚至沒有對象內部遞歸定義 - 它是在類級別定義。 – RealSkeptic

+0

@RealSkeptic好吧,爲了完整起見,也補充說。 – laune

0

在你的第一個代碼的情況下,Singleton類加載時static Singleton singleton = new Singleton();被初始化,每次致電Singleton.getInstance();只返回變量。

在第二代碼的情況下,存在一個infinite loop call,這將導致JVM無限期分配堆棧幀空間,並且每個堆棧大小分配後的限制,根據Java Virtual Machine Specification

If the computation in a thread requires a larger Java Virtual Machine stack than 
is permitted, the Java Virtual Machine throws a StackOverflowError. 

所以這肯定會導致stackoverflowerror。

相關問題