设计模式|单例模式(3) Enum枚举单例

上篇文章已经讨论了单例模式的安全问题。而枚举类型的单例模式是实现单例模式的最好的方法

参考:《Effective Java中文版》 p14-p15

只需编写一个包含单个元素的枚举类型。

代码

枚举类

public enum EnumSingleton {
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance(){
        return INSTANCE;
    }

}

测试类

/**
 * 枚举类型的单例模式测试
 */
@org.junit.Test
public void test08(){
    EnumSingleton enumSingleton = EnumSingleton.getInstance();
    enumSingleton.setData("we");
    System.out.println(enumSingleton); // INSTANCE
    EnumSingleton enumSingleton1 = EnumSingleton.getInstance();
    System.out.println(enumSingleton1); // INSTANCE
    System.out.println(enumSingleton1.getData()); //we
    System.out.println(enumSingleton == enumSingleton1); //true
}

反序列化攻击测试

/**
 * 枚举类型的单例模式测试:反序列化攻击
 */
@org.junit.Test
public void test09() throws Exception{
    /**
     * 正常获取实例
     */
    EnumSingleton enumSingleton = EnumSingleton.getInstance();
    enumSingleton.setData("we");
    System.out.println(enumSingleton.getData()); // we

    /**
     * 反序列化获取实例
     */
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enumSingleten"));
    oos.writeObject(enumSingleton);
    File file = new File("enumSingleten");
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
    EnumSingleton newInstance = (EnumSingleton)ois.readObject();
    System.out.println(newInstance.getData()); // we
    /**
     * 比较
     */
    System.out.println(newInstance.getData() == enumSingleton.getData());//true
}

同一个实例

反射攻击实例

/**
 * 枚举类型的单例模式测试:反射攻击
 */
@org.junit.Test
public void test10() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    /**
     * 正常获取实例
     */
    EnumSingleton enumSingleton = EnumSingleton.getInstance();
    enumSingleton.setData("we");
    System.out.println(enumSingleton.getData());

    /**
     * 反射获取实例
     */
    Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
    Constructor<EnumSingleton> constructor = enumSingletonClass.getDeclaredConstructor(String.class,int.class);
    constructor.setAccessible(true);
    /**
     * 如果newInstance()方法没有参数java.lang.NoSuchMethodException
     * 具体可看反射对枚举类型的处理方式
     */
    /**
     * 报错:
     * java.lang.IllegalArgumentException: Cannot reflectively create enum objects
     * 不能反射enum类型
     */
    EnumSingleton newInstance = constructor.newInstance("we",1);
    System.out.println(newInstance.getData());
 }

通过上面可以看到枚举类型可以防止反射攻击。

扩展1:在枚举类中声名方法

枚举类

public enum EnumSingleton{
    INSTANCE{
        protected void printTest(){
            System.out.println("test");
        }
    };
    protected abstract void printTest();
 }

测试

/**
 * 枚举类调用方法
 */
@org.junit.Test
public void test11(){
    EnumSingleton enumSingleton = EnumSingleton.getInstance();
    enumSingleton.printTest();
}

你可能感兴趣的:(设计模式)