爪哇国。懒惰地初始化Singleton[英] Java: Lazy Initializing Singleton

本文是小编为大家收集整理的关于爪哇国。懒惰地初始化Singleton的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

创建单例的模式似乎是:

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

    public static Singleton getInstance()
    {
        return instance;
    }
}

但是,我的问题是,如果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;
     }
}

现在的问题是,仅对于单位可测试性,我就强迫GetInstance同步,因此仅用于测试方面,它将对实际应用产生负面影响.有没有解决方案的方法,由于Java中双重锁定模式的断裂性质,似乎其他任何懒惰的初始化都无法正常工作.

推荐答案

您可以使用 factory模式创建singleleton,并根据Switch实现关于环境.

或避免使用单例图案,并使用依赖项注入代替.

.

其他推荐答案

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

enum Singleton {
    INSTANCE;
}

说您的单身人士在单位测试中做了不良的事情,您可以;

// 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.
        }
    }
}

注意:无需同步块或明确的锁定.直到使用成员之前,该实例将不会加载.前提


编辑:如果仅使用Singleton.class,这可能不会初始化类.在Java 8 Update 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");
        }
    }
}

打印

Printing a class reference
clazz = class ClassInitMain$Singleton

Using an enum value
class ClassInitMain$Singleton initialised

其他推荐答案

双检查锁定的每种语言都会损坏,而不仅仅是Java.

我倾向于避开单例,但是如果需要的话,您可以使用持有人图案,如乔什·布洛赫(Josh Bloch)的有效的java :

所建议的那样.
public class Foo
{
  static class Holder
  {
    static final Foo instance = new Foo();
  }

  public static Foo getInstance()
  {
    return Holder.instance;
  }

  private Foo()
  {
  }

  // ...
}

编辑:修复了参考.

本文地址:https://www.itbaoku.cn/post/627705.html

问题描述

The pattern to create singletons seems to be something like:

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

    public static Singleton getInstance()
    {
        return instance;
    }
}

However my problem is how do you Unit with a class like this if the Singleton Constructor does something that is not unit test friendly e.g. calls external service , jndi lookup etc.

I would think i could refactor it like:

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

The problem now is that just for unit testability I have forced the getInstance to be synchronized so just for testing aspect it will have a negative impact on the real application. Is there a way around it, it seems any other sort of lazy initialization will not work because of the broken nature of double locking pattern in java.

推荐答案

You could use the Factory pattern to create the singleton, and switch implementations depending on evironment.

Or, avoid using the singleton pattern, and use Dependency Injection instead.

其他推荐答案

You can use an enum as a Singleton

enum Singleton {
    INSTANCE;
}

Say your singleton does something undesirable in unit tests, you can;

// 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.
        }
    }
}

Note: there is no synchronised blocks or explicit lock needed. The INSTANCE will not be loaded until the .class is accessed and not initialised until a member is used. provided you only use Singleton.INSTANCE and not Singleton.class there won't be a problem with the value used to initialise changing later.


Edit: if you use just the Singleton.class this might not initialise the class. It doesn't in this example on Java 8 update 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");
        }
    }
}

prints

Printing a class reference
clazz = class ClassInitMain$Singleton

Using an enum value
class ClassInitMain$Singleton initialised

其他推荐答案

Double-checked locking is broken in every language, not just Java.

I tend to eschew singletons, but you can use the holder pattern just fine if you need them, as recommended in Josh Bloch's Effective Java:

public class Foo
{
  static class Holder
  {
    static final Foo instance = new Foo();
  }

  public static Foo getInstance()
  {
    return Holder.instance;
  }

  private Foo()
  {
  }

  // ...
}

EDIT: Repaired the reference.