java 单例模式_深入分析Java单例模式与序列化之间的爱恨情仇

首先我们先了解序列化的知识点,参考:

面试官:Java序列化为什么要实现Serializable接口?我懵了

单例模式的实现

public class Singleton {    private Singleton() {}    //2.本类内部创建对象实例    private static  volatile  Singleton instance;    //3.提供一个公有的静态方法,返回实例对象    public static  Singleton getInstance() {        if(instance == null) {            synchronized (Singleton.class) {                if(instance == null) {                    instance = new Singleton();                }            }        }        return instance;    }    public static void main( String[] args ) {        for(int i=0;i<100;i++){            new Thread(()->{                for(int j=0;j<100;j++){                    System.out.println(Singleton.getInstance());                }            }).start();        }    }}

上面我们使用多线程并发测试,测试结果如下:

48cb1baceeb2bfa4da93ebf6574abb9d.png

下面我们使用序列化来测试单例模式:

首先Singleton 需要实现 Serializable;

public class Main2 {    public static void main( String[] args ) throws IOException, ClassNotFoundException {        Singleton originSingleton = Singleton.getInstance();        ByteArrayOutputStream bos = new ByteArrayOutputStream();        ObjectOutputStream oos = new ObjectOutputStream(bos);        Singleton singleton = Singleton.getInstance();        System.out.println("原对象:"+singleton);        oos.writeObject(singleton);        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());        ObjectInputStream ois = new ObjectInputStream(bis);        Singleton serializeSingleton = (Singleton) ois.readObject();        System.out.println("反序列化的对象:"+serializeSingleton);    }}

测试结果:序列化和反序列化的对象不是同一个对象:

a9078d1d3da64792cef95ca35b4e4ca4.png

那么为什么会出现这种问题呢?

ObjectInputStream 的 readObject方法分析

 private Object readOrdinaryObject(boolean unshared)        throws IOException    {        if (bin.readByte() != TC_OBJECT) {            throw new InternalError();        }        ObjectStreamClass desc = readClassDesc(false);        desc.checkDeserialize();        Class> cl = desc.forClass();        if (cl == String.class || cl == Class.class                || cl == ObjectStreamClass.class) {            throw new InvalidClassException("invalid class descriptor");        }        Object obj;        try {            obj = desc.isInstantiable() ? desc.newInstance() : null;        } catch (Exception ex) {            throw (IOException) new InvalidClassException(                desc.forClass().getName(),                "unable to create instance").initCause(ex);        }        passHandle = handles.assign(unshared ? unsharedMarker : obj);        ClassNotFoundException resolveEx = desc.getResolveException();        if (resolveEx != null) {            handles.markException(passHandle, resolveEx);        }        if (desc.isExternalizable()) {            readExternalData((Externalizable) obj, desc);        } else {            readSerialData(obj, desc);        }        handles.finish(passHandle);        if (obj != null &&            handles.lookupException(passHandle) == null &&            desc.hasReadResolveMethod())        {            Object rep = desc.invokeReadResolve(obj);            if (unshared && rep.getClass().isArray()) {                rep = cloneArray(rep);            }            if (rep != obj) {                // Filter the replacement object                if (rep != null) {                    if (rep.getClass().isArray()) {                        filterCheck(rep.getClass(), Array.getLength(rep));                    } else {                        filterCheck(rep.getClass(), -1);                    }                }                handles.setObject(passHandle, obj = rep);            }        }        return obj;    }

我们抓重点,看下这句代码 obj = desc.isInstantiable() ? desc.newInstance() : null;,我们跟进 desc.isInstantiable()这个方法,看方法注释,简单来说一个类是serializable/externalizable的实例则返回true。

 /**     * Returns true if represented class is serializable/externalizable and can     * be instantiated by the serialization runtime--i.e., if it is     * externalizable and defines a public no-arg constructor, or if it is     * non-externalizable and its first non-serializable superclass defines an     * accessible no-arg constructor.  Otherwise, returns false.     */    boolean isInstantiable() {        requireInitialized();        return (cons != null);    }

为true的话,那么obj=desc.newInstance(),通过查看我们知道这个最终是调用的反射机制生成的新的实例。所以又重新生成了一个新类,那么怎么抵御单例的破坏呢?

前面生成了一个新类,我们继续往下看源码:

if (obj != null &&            handles.lookupException(passHandle) == null &&            desc.hasReadResolveMethod()){    Object rep = desc.invokeReadResolve(obj);}

着看desc.hasReadResolveMethod()这个方法,简单来说,就是该类是serializable or externalizable的实例,并且定义了符合要求的readResolve 方法,则返回true;

/**     * Returns true if represented class is serializable or externalizable and     * defines a conformant readResolve method.  Otherwise, returns false.     */    boolean hasReadResolveMethod() {        requireInitialized();        return (readResolveMethod != null);    }

那么 readResolveMethod 是什么方法呢 ?我们搜索一下:

    readResolveMethod = getInheritableMethod(                        cl, "readResolve", null, Object.class);

Object rep = desc.invokeReadResolve(obj);反射调用该方法。具体的就是

private Object readResolve()    {        return instance;    }

防止序列化破坏的解决方案

上面我们分析了源码,我们只要在我们的代码中加上 readResolve方法即可,代码如下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值