1. 程式人生 > >設計模式|單例模式(3) Enum列舉單例

設計模式|單例模式(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();
}