【单例深思】单例与序列化
来源:互联网 发布:mac终端编辑文件 编辑:程序博客网 时间:2024/06/06 04:20
在前面的文章中提到,序列化会破坏单例模式,下面用静态内部类的实现方式,说明序列化对单例的影响:
public class Singleton implements Serializable{
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
判断反序列化后的对象与原来的实例是否为同一个对象?
public class Test {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("storageFile"));
oos.writeObject(Singleton.getInstance());
// 反序列化
File file = new File("storageFile");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Singleton newInstance = (Singleton) ois.readObject();
//判断是否是同一个对象
System.out.println(newInstance == Singleton.getInstance());
}
} 输出结果为:false
说明反序列化后的实例和之前序列化的实例不是同一个对象,这就可能导致JVM中出现多个实例的情况,这样就违背了单例模式的要求。怎么解决这个问题呢?
之前的文章简单提了一下,给单例实现添加readResolve()方法即可,修改后的代码如下所示:
public class Singleton implements Serializable{
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
private Object readResolve() {
return SingletonHolder.INSTANCE;
}
}相比之前只有一处改动,就是添加readResolve()方法,在方法内返回了单例,重新运行一下上述的TEST类Main方法,返回结果为:true。
结果表明,反序列化后的实例和之前被序列化的实例是同一个对象,通过这个方法确实可以解决序列化对单例模式的影响。
其他的实现方式也一样可以通过这个方法解决序列化问题。
下面我们深入了解一下readResolve()方法为什么能解决这个问题?
首先,我们需要明白在没有添加这个方法之前,为什么返回了一个新的对象实例?
简单地说,这个新对象是在反序列化过程中,通过反射调用无参数的构造方法创建的。
从源码角度看,使用ObjectInputStream进行反序列化时,会使用它的readObject()方法。
public final Object readObject()
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(false); // obj 是要返回的对象
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
}
} 在readObject()方法内部,通过调用readObject0()方法返回需要被返回的对象。
private Object readObject0(boolean unshared) throws IOException {
boolean oldMode = bin.getBlockDataMode();
if (oldMode) {
int remain = bin.currentBlockRemaining();
if (remain > 0) {
throw new OptionalDataException(remain);
} else if (defaultDataEnd) {
/*
* Fix for 4360508: stream is currently at the end of a field
* value block written via default serialization; since there
* is no terminating TC_ENDBLOCKDATA tag, simulate
* end-of-custom-data behavior explicitly.
*/
throw new OptionalDataException(true);
}
bin.setBlockDataMode(false);
}
byte tc;
while ((tc = bin.peekByte()) == TC_RESET) {
bin.readByte();
handleReset();
}
depth++;
totalObjectRefs++;
try {
switch (tc) {
case TC_NULL:
return readNull();
case TC_REFERENCE:
return readHandle(unshared);
case TC_CLASS:
return readClass(unshared);
case TC_CLASSDESC:
case TC_PROXYCLASSDESC:
return readClassDesc(unshared);
case TC_STRING:
case TC_LONGSTRING:
return checkResolve(readString(unshared));
case TC_ARRAY:
return checkResolve(readArray(unshared));
case TC_ENUM:
return checkResolve(readEnum(unshared));
case TC_OBJECT:
return checkResolve(readOrdinaryObject(unshared)); // 处理对象序列化
case TC_EXCEPTION:
IOException ex = readFatalException();
throw new WriteAbortedException("writing aborted", ex);
case TC_BLOCKDATA:
case TC_BLOCKDATALONG:
if (oldMode) {
bin.setBlockDataMode(true);
bin.peek(); // force header read
throw new OptionalDataException(
bin.currentBlockRemaining());
} else {
throw new StreamCorruptedException(
"unexpected block data");
}
case TC_ENDBLOCKDATA:
if (oldMode) {
throw new OptionalDataException(true);
} else {
throw new StreamCorruptedException(
"unexpected end of block data");
}
default:
throw new StreamCorruptedException(
String.format("invalid type code: %02X", tc));
}
} finally {
depth--;
bin.setBlockDataMode(oldMode);
}
} 对象的序列化处理在 checkResolve(readOrdinaryObject(unshared)); 方法中进行。
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);
// 添加了readResolve()方法后,就执行判断内的代码
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj); // 调用readResolve方法
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; 这行代码中,会先判断是否实现了serializable/externalizable,如果实现了,就采用反射的方法实例化一个对象。
在这个方法内还有一个 if 判断,if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()),
其中 desc.hasReadResolveMethod() 用于判断被序列化的类中是否包含 readResolve() 方法,如果包含就返回true,在这个判断逻辑内,会执行desc.invokeReadResolve(obj);方法,该方法会反射调用我们声明的 readResolve() 方法,我们在该方法内返回了序列化的对象,因此我们也就明白了readResolve() 方法的工作原理了。
这种约定式的使用方式在 Java 中很常见的,通过这些分析我们明白了序列化机制对单例的影响,以及通过什么样的方法解决了这些影响。
0 0
- 【单例深思】单例与序列化
- 【单例深思】饿汉式与类加载
- 【单例深思】懒汉式改进版与内置锁
- 【单例深思】双重检测锁与Java内存模型
- 一次单例模式引发的深思
- 【单例深思】Java 单例模式全解
- 【单例深思】枚举实现单例原理
- java 单例与序列化
- 【Java】序列化与单例
- 【单例深思】静态内部类实现详解
- 【转载】JAVA序列化/反序列化与单例
- 单例的序列化与反序列化
- 单例与序列化的那些事儿
- 单例与序列化的那些事儿
- 单例与序列化的那些事儿
- 单例与序列化的那些事儿
- 单例与序列化的那些事儿
- 单例与序列化的那些事儿
- Servlet基础1
- WebAPI OWIN认证引起的系列问题
- 支付宝支付思路
- 适配器模式
- Android Gradle Build Error:Some file crunching failed, see logs for details解决办法
- 【单例深思】单例与序列化
- vlc的url中间加上ip
- hdu 5547 Sudoku
- yii2中controller向layout传值
- 腾讯云Ubuntu 14.04 & Apache 2.4.7 $ CI框架 之 如何去掉index.php后缀
- Java 并发编程深入学习——ThreadLocal 原理分析
- sudo apt-get update 与upgrade的用法
- js获取ip地址
- C++primer学习笔记-----4.9~4.10