单例模式的应用(2)

三、反射破坏单例


一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,下次使用时再从磁盘中读对象进行反序列化,将其转换为内存对象。反序列化后的对象会重新分配内存,即重新创建。如果序列化的目标对象为单例对象,就违背了单例模式的初衷,相当于破坏了单例,来看一段代码:


public class Singleton {  
    private static class LazyHolder {  
       private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
       return LazyHolder.INSTANCE;  
    }  
}  


//单例模式,通过反射获取实例


public class ReflectTest {

    public static void main(String[] args) {
        try {
            Class<?> clazz = Singleton.class;
            //获取该单例类的空构造器
            Constructor c = clazz.getDeclaredConstructor(null);
            //绕过私有权限
            c.setAccessible(true);

            Object instance1 = c.newInstance();
            Object instance2 = c.newInstance();
            System.out.println(instance1);
            System.out.println(instance2);
            System.out.println(instance1 == instance2);

        }catch (Exception e){
            e.printStackTrace();
        }
    }


通过反射获得单例类的构造函数,由于该构造函数是private的,通过setAccessible(true)指示反射的对象在使用时应该取消 Java 语言访问检查,使得私有的构造函数能够被访问,这样使得单例模式失效。


//自认为史上最牛的单例模式的实现方式
public class Singleton {  
    //使用LazyHolder的时候,默认会先初始化内部类
    //如果没使用,则内部类不加载的
    private static class LazyHolder {  
       private static final Singleton INSTANCE = new Singleton();  
    }  
    // 每一个关键字都不是多余的,static是为了使单例的空间共享,保证这个放啊不会被重写、重载
    private Singleton (){
       if(LazyHolder.INSTANCE!=null){
       //在返回结构以前,一定会先加载内部类
           throw new RuntimeException("不允许创建多个实例!");
       }
    }  
    // 默认不加载
    public static final Singleton getInstance() {  
       return LazyHolder.INSTANCE;  
    }  
}  


四、序列化破坏单例

一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,下次使用时再从磁盘中读取对象,并进行反序列化,将其转化为内存对象。反序列化后的对象会重新分配内存,即重新创建。如果序列化的目标对象为单例对象,就违背了单例模式的初衷,相当于违背了单例模式。


public class SeriableSingleton implements Serializable {


    //序列化
    //把内存中对象的状态转换为字节码的形式
    //把字节码通过IO输出流,写到磁盘上
    //永久保存下来,持久化

    //反序列化
    //将持久化的字节码内容,通过IO输入流读到内存中来
    //转化成一个Java对象


    public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}

    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }

}


public class SeriableSingletonTest {
    public static void main(String[] args) {

        SeriableSingleton s1 = null;
        SeriableSingleton s2 = SeriableSingleton.getInstance();

        FileOutputStream fos = null;
        try {

            fos = new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SeriableSingleton)ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


从运行结果可以看出,反序列化后的对象和手动创建的对象是不一致的,实例化了两次,违背了单例设计模式的初衷。那么,我们如何保证在序列化的情况下,也能够实现单例模式呢,其实很简单,只要增加readResolve()方法即可。


public class SeriableSingleton implements Serializable {


    //序列化
    //把内存中对象的状态转换为字节码的形式
    //把字节码通过IO输出流,写到磁盘上
    //永久保存下来,持久化

    //反序列化
    //将持久化的字节码内容,通过IO输入流读到内存中来
    //转化成一个Java对象


    public  final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}

    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }

    private Object readResolve(){ return INSTANCE;}

}



上一篇:工厂设计模式、简单工厂设计模式、抽象工厂设计模式(2)


下一篇:静态代理、动态代理(JDK动态代理,Cglib动态代理)(2)