接上回之讲,我们通过反射来破坏单例模式,那么还有什么方法呢?
序列化破坏单例模式
一个单例对象创建好后,有时候需要将对象序列化写入磁盘,下次使用时再从磁盘读取进行反序列化,转化成内存里的对象。反序列化后的对象需要重新分配内存空间,重新创建。我们来看一段代码:
public class Solution implements Serializable {
private static final Solution INSTANCE = new Solution();
private Solution() {
}
public static Solution getInstance() {
return INSTANCE;
}
}
class Test {
public static void main(String[] args) {
Solution s1 = Solution.getInstance();
Solution s2 = null;
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("solution.txt"));
outputStream.writeObject(s1);
outputStream.flush();
outputStream.close();
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("solution.txt"));
s2 = (Solution) inputStream.readObject();
inputStream.close();
System.out.println(s1 == s2);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果如下:
从运行结果看,反序列化后的对象和手动创建的对象是不一致的,违背了单例模式的初衷,那么,我们该如何保证在序列化的情况下也能实现单例模式呢?其实很简单,只需要增加readResolve()方法即可,来看优化后的代码:
public class Solution implements Serializable {
private static final Solution INSTANCE = new Solution();
private Solution() {
}
public static Solution getInstance() {
return INSTANCE;
}
public Object readResolve(){
return INSTANCE;
}
}
再看运行结果,结果如下:
大家一定会想,这是什么原因呢,为什么要这么写,不如我们来剖析一下JDK的源码来看看发生了什么。我们进入ObjectInputStream类的readObject()方法,代码如下:
private final Object readObject(Class<?> type)
throws IOException, ClassNotFoundException
{
if (enableOverride) {
return readObjectOverride();
}
if (! (type == Object.class || type == String.class))
throw new AssertionError("internal error");
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(type, false);
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()方法,进入readObject0()方法,代码如下:
private Object readObject0(Class<?> type, boolean unshared) throws IOException {
...
case TC_OBJECT:
if (type == String.class) {
throw new ClassCastException("Cannot cast an object to java.lang.String");
}
return checkResolve(readOrdinaryObject(unshared));
...
接下来我们进入readOrdinaryObject(unshared)方法:
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
...
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);
}
...
return obj;
}
我们发现调用了isInstantiable()方法,进去查看一下:
boolean isInstantiable() {
requireInitialized();
return (cons != null);
}
上述代码非常简单,就是判断构造函数是否为空,不为空就返回true,然后调用其无参构造函数实例化,但我们还没找到加上readResolve()方法就可以避免单例模式被破坏的真正原因,再继续往下看:
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);
}
...
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;
}
判断无参构造方法存在后,又调用了hasReadResolveMethod()方法,来看代码:
boolean hasReadResolveMethod() {
requireInitialized();
return (readResolveMethod != null);
}
就是判断readResolveMethod是否为空,不为空就返回true,现在继续往下看,如果存在readResolve()方法就通过反射调用其方法:
Object invokeReadResolve(Object obj)
throws IOException, UnsupportedOperationException
{
requireInitialized();
if (readResolveMethod != null) {
try {
return readResolveMethod.invoke(obj, (Object[]) null);
} catch (InvocationTargetException ex) {
Throwable th = ex.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException) th;
} else {
throwMiscException(th);
throw new InternalError(th); // never reached
}
} catch (IllegalAccessException ex) {
// should not occur, as access checks have been suppressed
throw new InternalError(ex);
}
} else {
throw new UnsupportedOperationException();
}
}
通过JDK源码我们可以看出,虽然增加这个方法解决了问题,但实际上实例化了两次,只不过新创建的没被返回而已,那么如何真正意义上的解决这个问题呢?
枚举式单例模式
public enum Solution {
INSTANCE;
public static Solution getInstance() {
return INSTANCE;
}
}
我们来看运行结果:
下个文章将从源码角度揭开它神奇的面纱,太晚了,肝不动了。。。