Java:延迟初始化单例
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5842273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java: Lazy Initializing Singleton
提问by bluphoenix
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.
然而,我的问题是,如果单例构造函数做了一些对单元测试不友好的事情,例如调用外部服务、jndi 查找等,你如何用这样的类来单元。
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.
现在的问题是,仅仅为了单元可测试性,我已经强制同步 getInstance,所以仅仅为了测试方面,它会对实际应用程序产生负面影响。有没有办法解决它,由于java中双重锁定模式的破坏性质,似乎任何其他类型的延迟初始化都不起作用。
采纳答案by Joel
You could use the Factory patternto create the singleton, and switch implementations depending on evironment.
您可以使用工厂模式来创建单例,并根据环境切换实现。
Or, avoid using the singleton pattern, and use Dependency Injectioninstead.
或者,避免使用单例模式,而使用依赖注入。
回答by Peter Lawrey
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.
注意:不需要同步块或显式锁。在访问 .class 之前不会加载 INSTANCE 并且在使用成员之前不会初始化。如果您只使用Singleton.INSTANCE
而不是Singleton.class
稍后用于初始化更改的值不会有问题。
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.
编辑:如果您只使用Singleton.class
this 可能不会初始化该类。在 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");
}
}
}
prints
印刷
Printing a class reference
clazz = class ClassInitMain$Singleton
Using an enum value
class ClassInitMain$Singleton initialised
回答by Lew Bloch
Double-checked locking is broken in every language, not just Java.
双重检查锁定在每种语言中都被破坏,而不仅仅是 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:
我倾向于避开单身人士,但如果需要,您可以很好地使用持有人模式,正如 Josh Bloch 的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.
编辑:修复了参考。
回答by Lawrence McAlpin
You can dependency inject the singleton instance, override the getInstance() from the unit test code, use aspect oriented programming to intercept the method call and return a different object, or use a tool like jmockitwhich lets you mock pretty much anything, including statics, final classes, constructors, and all the stuff people normally say is "untestable."
您可以依赖注入单例实例,覆盖单元测试代码中的 getInstance(),使用面向方面的编程来拦截方法调用并返回不同的对象,或者使用像jmockit这样的工具,它可以让您模拟几乎任何东西,包括静态、final 类、构造函数以及人们通常所说的“不可测试”的所有东西。
One approach I've taken in legacy systems (where I wanted to make something testable with a minimal impact on the system's architecture) was to modify the factory methods (getInstance) to check a system property for an alternate implementation that I would instantiate instead. This was set to an alternate, mock object in the unit test suite.
我在遗留系统中采用的一种方法(我希望在对系统架构的影响最小的情况下使某些东西可测试)是修改工厂方法 (getInstance) 以检查系统属性以获取我将实例化的替代实现。这被设置为单元测试套件中的备用模拟对象。
As for the "double checked locking is broken" statement, that's not really true anymore, if you use the volatile keyword, and Java >= 1.5. It was broken (even with volatile) with 1.4 and earlier, but if you know your code will be run on only recent JVMs, I wouldn't worry about it. But I also wouldn't use a singleton anyway: having a DI/IOC container manage the lifecycle of the object would solve both of your problems (testability and synchronized accessor bottleneck) much more elegantly.
至于“双重检查锁定被破坏”的说法,如果您使用 volatile 关键字,并且 Java >= 1.5,那不再是真的了。它在 1.4 及更早版本中被破坏(即使是 volatile),但如果您知道您的代码将仅在最近的 JVM 上运行,我就不会担心。但无论如何我也不会使用单例:让 DI/IOC 容器管理对象的生命周期将更优雅地解决您的两个问题(可测试性和同步访问器瓶颈)。
回答by simpatico
How about you lazy initialize in the build phase where you execute the unit tests. Then you change the code back to inline initialize before it's compiled for distribution.
在执行单元测试的构建阶段延迟初始化如何。然后在编译分发之前将代码改回内联初始化。
Your production code is inline initialized, except during your tests. Perhaps this discrepancy btw production and testing code could introdude bugs, but which?
您的生产代码已内联初始化,但在您的测试期间除外。顺便说一句,生产和测试代码的这种差异可能会引入错误,但是哪个?
(Of course if this is a solution, we let a build phase + tool do the work. I see this facilitated with maven and dp4j).