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


当前回答

Java 5+中的线程安全:

class Foo {
    private static volatile Bar bar = null;
    public static Bar getBar() {
        if (bar == null) {
            synchronized(Foo.class) {
                if (bar == null)
                    bar = new Bar();
            }
        }
        return bar;
    }
}

注意此处的挥发性修饰语。:)这很重要,因为如果没有它,JMM(Java内存模型)就不能保证其他线程看到其值的更改。同步不会处理这一点——它只序列化对该代码块的访问。

@Bno的回答详细介绍了Bill Pugh(FindBugs)推荐的方法,并且可以更好地进行论证。去阅读并投票支持他的答案。

其他回答

使用枚举:

public enum Foo {
    INSTANCE;
}

乔舒亚·布洛赫(Joshua Bloch)在Google I/O 2008的“有效Java重载”演讲中解释了这种方法:视频链接。另请参见其演示文稿的幻灯片30-32(effecte_java_reloaded.pdf):

实现可串行化Singleton的正确方法公共枚举Elvis{实例;private final String[]收藏夹歌曲={“猎犬”,“心碎酒店”};public void printFavorites(){System.out.println(Arrays.toString(收藏夹歌曲));}}

编辑:“有效Java”的在线部分说:

“这种方法在功能上等同于公共字段方法,只是它更加简洁,免费提供了序列化机制,并且即使在复杂的序列化或反射攻击的情况下也能提供针对多个实例化的铁腕保证。虽然这种方法尚未被广泛采用,但单个元素枚举类型是实现一个公共字段的最佳方式。”英格顿。"

这是如何实现一个简单的单例:

public class Singleton {
    // It must be static and final to prevent later modification
    private static final Singleton INSTANCE = new Singleton();
    /** The constructor must be private to prevent external instantiation */
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

这是如何正确地延迟创建单例:

public class Singleton {
    // The constructor must be private to prevent external instantiation
    private Singleton(){}
    /** The public static method allowing to get the instance */
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
    /**
     * The static inner class responsible for creating your instance only on demand,
     * because the static fields of a class are only initialized when the class
     * is explicitly called and a class initialization is synchronized such that only
     * one thread can perform it, this rule is also applicable to inner static class
     * So here INSTANCE will be created only when SingletonHolder.INSTANCE
     * will be called
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

确保你真的需要它。在谷歌上搜索“singleton反模式”,看看反对它的一些论点。

我想这并没有什么本质上的问题,但它只是一种公开某些全局资源/数据的机制,因此请确保这是最好的方法。特别是,我发现依赖注入(DI)更有用,特别是如果您也在使用单元测试,因为DI允许您将模拟资源用于测试目的。

最简单的单例类:

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

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

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

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