首先我们先了解序列化的知识点,参考:
面试官: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(); } }}
上面我们使用多线程并发测试,测试结果如下:
下面我们使用序列化来测试单例模式:
首先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); }}
测试结果:序列化和反序列化的对象不是同一个对象:
那么为什么会出现这种问题呢?
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方法即可,代码如下: