java enum实现的单例

为了防止通过反序列化得到多个对象,EJ提倡使用enum实现单例:
关于枚举的对象为什么可以反序列化:可以看Enum类的如下方法:
Java代码

/** 
     * prevent default deserialization 
     */  
   private void readObject(ObjectInputStream in) throws IOException,  
       ClassNotFoundException {  
           throw new InvalidObjectException("can't deserialize enum");  
   }  

   private void readObjectNoData() throws ObjectStreamException {  
       throw new InvalidObjectException("can't deserialize enum");  
   }  

Java代码

import java.io.FileInputStream;  
import java.io.FileOutputStream;  
import java.io.ObjectInputStream;  
import java.io.ObjectOutputStream;  
import java.io.Serializable;  
public class Test {  
    public static void main(String[] args) throws Exception {  
        Singleton d1 = Singleton.INSTANCE;  
        d1.setName("a fucker.");  
        System.out.println(d1);  

        FileOutputStream fos = new FileOutputStream("out.data");  
        ObjectOutputStream oos = new ObjectOutputStream(fos);  
        oos.writeObject(d1);  
        fos.close();  
        oos.close();  

        FileInputStream fis = new FileInputStream("out.data");  
        ObjectInputStream ois = new ObjectInputStream(fis);  
        Object o = ois.readObject();  
        fis.close();  
        ois.close();  

        Singleton d2 = (Singleton)o;  

        System.out.println(d2);  
        System.out.println(d1 == d2);  
    }  
}  
enum Singleton implements Serializable {  

    INSTANCE;  

    private String name;  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    @Override  
    public String toString() {  
        return "[" + name + "]";  
    }  
}  

因为一个enum常量(这里是INSTANCE)代表了一个enum的实例,enum类型只能有这些常量实例。标准保证enum常量(INSTANCE)不能被克隆,也不会因为反序列化产生不同的实例,想通过反射机制得到一个enum类型的实例也不行的。

如果用一般方式写单例模式,该单例类如果实现了Serializable接口,则必须添加readResolve()方法,当然我认为按照Enum类的设计在 readObject(ObjectInputStream in)中抛出异常也可以有效防止反序列化:
Java代码

public class Singleton {  

    private static volatile Singleton st;  

    private Singleton(){};  

    public static Singleton getInstance() {  
        if (null == st) {  
            synchronized (Singleton.class) {  
                if (st == null) {  
                    st = new Singleton();  
                    return st;  
                }  
            }  
        }  
        return st;  
    }  

    /** 
     * 反序列化时内存Hook这段代码 
     * @return 
     */  
    private Object readResolve() {  
        return st;  
    }  
}  

Java代码

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;  
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;  

这两个方法可以理解为序列化和反序列化过程的入口和出口。writeReplace()返回的对象,就是要被序列化的对象,我们有机会在序列化前把这个对象给换成我们确定好的那个(如果不是“故意捣乱”,暂时没想到有什么用);而readResolve()方法就是在反序列化完成得到对象前,把这个对象给换成我们确定好的那个。

明白了吧?为了防止有人恶意通过序列化的机制破坏定义好的单例,我们就需要自己实现readResolve()方法,把单例定义的唯一实现在这个方法中返回。

原文地址:http://837062099.iteye.com/blog/1454934

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值