这里的加了枚举的单例模式是否有问题

来源:5-6 无视反射和序列化攻击的单例

慕数据4154996

2020-03-26

先给出老师的饿汉式枚举封装的单例代码。

public class EnumStarvingSingleton {
    private EnumStarvingSingleton(){}

    public static EnumStarvingSingleton getInstance(){
        return ContainerHolder.HOLDER.instance;
    }

    private enum ContainerHolder{
        HOLDER;
        private EnumStarvingSingleton instance;
        ContainerHolder(){
            instance = new EnumStarvingSingleton();
        }
    }
}

再给出我的测试用例:

public class SingletonDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor constructor = EnumStarvingSingleton.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        EnumStarvingSingleton instance1 = (EnumStarvingSingleton) constructor.newInstance();
        EnumStarvingSingleton instance2 = EnumStarvingSingleton.getInstance();
        System.out.println(instance1 == instance2);
    }
}

我的测试用例结果是false,说明反射已经破坏了该单例模式。
一般用枚举类型抵御反射对单例的破坏,直接用枚举就行了,如下述代码所示:

public enum Singleton {
    INSTANCE
}

测试代码如下:

public class SingletonDemo {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Constructor constructor = Singleton.class.getDeclaredConstructor(String.class, int.class);
        constructor.setAccessible(true);
        Singleton instance1 = (Singleton) constructor.newInstance();
        Singleton instance2 = Singleton.INSTANCE;
        System.out.println(instance1 == instance2);
    }
}

上述测试会抛出IllegalArgumentException异常,因为枚举类无法通过反射来创建实例。
不知道为什么老师这里要多一层封装,把枚举作为私有的内部类来处理?

写回答

1回答

翔仔

2020-03-26

同学好,你的这个变量名字好有迷惑性呀。。

        Constructor constructor = EnumStarvingSingleton.class.getDeclaredConstructor();
        EnumStarvingSingleton instance1 = (EnumStarvingSingleton) constructor.newInstance();

这个是EnumStarvingSingleton实例本身吧。。

EnumStarvingSingleton instance2 = EnumStarvingSingleton.getInstance();

EnumStarvingSingleton instance2 = EnumStarvingSingleton.getInstance(); 这个才是枚举的实例呢。。所以针对枚举的实例来讲,是单例的呀。

这里用内部枚举的原因主要就是通过内部枚举持有容器单例。保证持有的容器单例是不会被破坏的。。

0
5
翔仔
回复
慕数据4154996
是可以的呢,主要是将万物都设计成对象比较好些,看起来容易理解,主要是这个原因:)
2020-03-28
共5条回复

剑指Java自研框架,决胜Spring源码

快速入门Spring核心源码+从零开发自研框架

1498 学习 · 495 问题

查看课程