使用反射防止开裂单例

rob*_*bin 1 java design-patterns

我遇到了以下示例,其中可以使用反射来实例化单例类。代码如下

public class SingletonExploitationExample {

    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        MySingleTon.getInstance();
        Constructor<MySingleTon> constructor = MySingleTon.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        MySingleTon obj1 = constructor.newInstance();
        MySingleTon obj2 = constructor.newInstance();
        MySingleTon obj3 = constructor.newInstance();
        obj1.printN();
    }
}

final class MySingleTon {
    private static MySingleTon instance = null;

    private static int count = 0;
    private MySingleTon(){
        count++;
    }

    public void printN(){
        System.out.println(count);
    }

    public static MySingleTon getInstance(){
        if(instance == null){
            synchronized (MySingleTon.class){
                if(instance == null){
                    instance = new MySingleTon();
                }

            }
        }
        return instance;
    }
}
Run Code Online (Sandbox Code Playgroud)

有什么办法可以避免这种情况,或者使一个实例只有一个实例(通过反射用户也不能实例化一个新对象)?

Men*_*ena 5

是的,带enums!

枚举使功能强大的线程安全,“类加载器安全”的单例成为可能,并且无法通过反射进行初始化!

这是一个例子。

enum Foo {
    INSTANCE;
}

Class<?> fooClass = Foo.class;
Constructor<?> c = fooClass.getDeclaredConstructors()[0];
c.setAccessible(true);
Foo f = (Foo)c.newInstance();
Run Code Online (Sandbox Code Playgroud)

输出量

java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at ...
Run Code Online (Sandbox Code Playgroud)

  • 好吧,反思有点像客厅的把戏。您已经可以做很多事情了。但是,如果您使用`sun.misc.Unsafe.allocateInstance(Foo.class);`来开枪,那可能很好用。因此,假设您可以在Java中实际上将敏感信息保密是一个好主意。 (2认同)
  • @robin读了我的答案。如上使用时,枚举是单例。如果您希望保持相同的语法,可以添加一个便捷方法`getInstance`以返回唯一的实例(`INSTANCE`),等等。但是实际上,您只需要将“ MySingleton”重构为一个“ enum”即可。 (2认同)