在Java中实现单例设计模式的有效方法是什么?


当前回答

忘记延迟初始化;这太有问题了。这是最简单的解决方案:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

其他回答

我对一些答案感到困惑,这些答案建议依赖注入(DI)作为使用单态的替代方案;这些都是不相关的概念。您可以使用DI注入单例或非单例(例如,每个线程)实例。至少如果您使用Spring2.x,这是正确的,我不能为其他DI框架说话。

所以我对OP的回答是(除了最简单的示例代码之外):

使用类似Spring framework的DI框架,然后将其作为DI配置的一部分,无论依赖项是单体的、请求范围的、会话范围的还是其他。

这种方法为您提供了一个很好的解耦(因此是灵活和可测试的)架构,其中是否使用单例是一个容易可逆的实现细节(当然,前提是您使用的任何单例都是线程安全的)。

有四种方法可以在Java中创建单例。

Eager初始化单例公共类测试{私有静态最终测试=新测试();专用测试(){}公共静态测试getTest(){回归试验;}}惰性初始化单例(线程安全)公共类测试{私人静态挥发性测试;专用测试(){}公共静态测试getTest(){if(测试==空){同步(Test.class){if(测试==空){test=新测试();}}}回归试验;}}Bill Pugh单例与持有者模式(最好是最好的一个)公共类测试{专用测试(){}私有静态类TestHolder{私有静态最终测试=新测试();}公共静态测试getInstance(){return TestHolder.test;}}枚举单例公共枚举MySingleton{实例;私有MySingleton(){System.out.println(“此处”);}}

忘记延迟初始化;这太有问题了。这是最简单的解决方案:

public class A {    

    private static final A INSTANCE = new A();

    private A() {}

    public static A getInstance() {
        return INSTANCE;
    }
}

如果需要惰性地加载类的实例变量,则需要双重检查习惯用法。如果需要惰性地加载静态变量或单例,则需要按需初始化持有者习惯用法。

此外,如果单例需要可序列化,则所有其他字段都需要是暂时的,并且需要实现readResolve()方法以保持单例对象不变。否则,每次反序列化对象时,都会创建对象的新实例。readResolve()所做的是用readObject()替换读取的新对象,因为没有变量引用该对象,所以强制对该新对象进行垃圾回收。

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // Original singleton instance.
} 

在写它之前,真的要考虑一下为什么你需要一个单例。关于使用它们有一个准宗教的争论,如果你在Java中搜索单例,你很容易就会发现。

就我个人而言,出于许多原因,我尽量避免单身汉,其中大部分可以通过谷歌搜索单身汉找到。我觉得单身汉经常被虐待,因为他们很容易被每个人理解。它们被用作将“全局”数据引入OO设计的机制,因为它很容易绕过对象生命周期管理(或者真正思考如何从B内部实现a)。看看像控制反转(IoC)或依赖注入(DI)这样的事情,可以找到一个不错的中间地带。

如果你真的需要一个,那么Wikipedia有一个很好的例子来说明单例的正确实现。