我最近遇到过这种代码。
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函数并在主类中调用它之前。)
答案 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错误。