Java:懒惰初始化单例

blu*_*nix 7 java singleton synchronization design-patterns

创建单身人士的模式似乎是这样的:

public class Singleton {
    private static final Singleton instance = new Singleton();
    private Singleton(){
    }

    public static Singleton getInstance()
    {
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我的问题是如果Singleton构造函数执行非单元测试友好的事情,例如调用外部服务,jndi查找等,你如何使用这样的类.

我想我可以重构它:

public class Singleton {
    private static Singleton instance;
    private Singleton(){
    }

    public synchronized static Singleton getInstance()
    {
        if(instance == null)
             instance = new Singleton();
        return instance;
    }

     //for the unit tests
     public static void setInstance(Singleton s)
     {
          instancce = s;
     }
}
Run Code Online (Sandbox Code Playgroud)

现在的问题是,仅仅为了单元可测试性,我已经强制getInstance被同步,因此只是对于测试方面,它将对实际应用程序产生负面影响.有没有办法解决它,似乎任何其他类型的延迟初始化将无法工作,因为java中双锁模式的破坏性质.

Pet*_*rey 11

您可以将枚举用作单身人士

enum Singleton {
    INSTANCE;
}
Run Code Online (Sandbox Code Playgroud)

假设您的单身人士在单元测试中做了不受欢迎的事情,你可以;

// in the unit test before using the Singleton, or any other global flag.
System.setProperty("unit.testing", "true");

Singleton.INSTANCE.doSomething();

enum Singleton {
    INSTANCE;
    {
        if(Boolean.getBoolean("unit.testing")) {
           // is unit testing.
        } else {
           // normal operation.
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:不需要同步块或显式锁定.在访问.class之前不会加载INSTANCE,并且在使用成员之前不会初始化.如果您只使用Singleton.INSTANCE而不是Singleton.class以后用于初始化更改的值不会出现问题.


编辑:如果你只使用Singleton.class这可能不会初始化类.它不在Java 8更新112的这个例子中.

public class ClassInitMain {
    public static void main(String[] args) {
        System.out.println("Printing a class reference");
        Class clazz = Singleton.class;
        System.out.println("clazz = " + clazz);
        System.out.println("\nUsing an enum value");
        Singleton instance = Singleton.INSTANCE;
    }

    static enum Singleton {
        INSTANCE;

        Singleton() {
            System.out.println(getClass() + " initialised");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

版画

Printing a class reference
clazz = class ClassInitMain$Singleton

Using an enum value
class ClassInitMain$Singleton initialised
Run Code Online (Sandbox Code Playgroud)


Joe*_*oel 6

您可以使用Factory模式创建单例,并根据环境切换实现.

或者,避免使用单例模式,而是使用依赖注入.