最近观看effective in java ,提到单例模式创建过程中,如果是要保证该对象是可序列化的,需要考虑两点:
1、继承Serializable接口
2、增加readResolve方法
比较疑惑的是为什么需要增加这个方法,在以往的使用中需要被序列化的场景也不多,但是自己确实不明白这个单例对象在反序列化的时候会导致增加一个假冒的对象,从而’单例变的也不在单例‘
深入到代码细节观察发现:
ObjectInputStream反序列化会利用ObjectStreamClass序列化描述符创建一个实例
1、如果实例不为空
2、且描述符内检测到含有readResolve方法
3、反序列化中没有异常发生
满足以上条件会反射执行readResolve获取实例对象,并且和先前的对象作比较,不相等,用本次的值覆盖先前的返回值
不满足以上条件直接返回实例对象,完成反序列化
ObjectInputStream源码如下(标红部分):
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
if (bin.readByte() != TC_OBJECT) {
throw new InternalError();
}
ObjectStreamClass desc = readClassDesc(false);
desc.checkDeserialize();
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) {
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
-----------------------------------------------------------------------------------------------------------------------
测试程序如下:
package com.tt.st;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Singleton implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2090309963475550553L;
private static final Singleton instance = new Singleton();
private Singleton() {
System.out.println(System.currentTimeMillis());
}
public static Singleton getInstance() {
return instance;
}
private Object readResolve() throws ObjectStreamException {
return instance;
}
}
package com.tt.st;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Main {
private static native ClassLoader latestUserDefinedLoader();
public static void main(String[] args) throws Exception{
// TODO Auto-generated method stub
File file = new File("d:\\doc\\ab.out");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
Singleton singleton = Singleton.getInstance();
System.out.println("first: " + singleton);
objectOutputStream.writeObject(singleton);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Object object = objectInputStream.readObject();
System.out.println("second: " + object);
objectInputStream.close();
}
}