单例:在构造函数中抛出异常



为了防止使用反射破坏Singleton,一种方法是在私有构造函数中抛出异常,如下面的代码所示:

public final class Foo {
    private static final Foo INSTANCE = new Foo();
    private Foo() {
        if (INSTANCE != null) {
            throw new IllegalStateException("Already instantiated");
        }
    }
    public static Foo getInstance() {
        return INSTANCE;
    }
}

上面是一个标准的代码,但我想的是,它是线程安全的吗?如果多个线程尝试同时使用反射创建实例[即在类加载到主存之前,这意味着实例将为null],那么它们会成功吗?

在装入类之前,不能访问类的静态成员(无论是直接访问还是通过反射访问)。静态final成员在加载过程中初始化(JLS描述的第9步)。

所以在你的情况下,线程不可能:

  • 在正确构建之前参见INSTANCE
  • 看到INSTANCE为空(除非第一次调用new Foo()抛出异常)
  • 初始化第二个实例(除非该代码依赖于不同的类加载器)

创建一个反射安全的单例的最好方法是使用enum。

最新更新