为什么这个Singleton模式不会导致StackOverflowError?

时间:2015-01-04 16:04:22

标签: java singleton

我最近遇到过这种代码。

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");
    }
}  

现在,乍一看这个问题可能并不明显。至少不是我:p
所以,添加此功能可以使问题足够清晰

    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。(即在添加print函数并在主类中调用它之前。)

4 个答案:

答案 0 :(得分:2)

您混淆的根源在于您认为单例定义是递归的。实际上并非如此。

private static Singleton singleton = new Singleton();

这是 static 字段的定义。这意味着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的定义更改为静态,错误将消失。

答案 1 :(得分:0)

这里有两种不同的场景。当Singleton类本身被加载时,Singleton的单例将被初始化一次。因此,当您在单例上调用getInstance时,它不会重新初始化,而是会一次又一次地返回相同的实例。

你的第二个例子是一个递归调用,调用它本身没有任何退出条件,因此导致StackOverflowError。

答案 2 :(得分:0)

您可能还会混淆递归的对象关系:

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

使用方法的动态递归。

如果构造它们而不会遇到另一种类型的递归,那么像Foo这样的类就没有问题,例如,

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

答案 3 :(得分: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.

所以这显然会导致stackoverflower错误。