var4;
...
} finally {
--this.depth;
this.bin.setBlockDataMode(oldMode);
}
return var4;
}
我们看到代码中调用了 ObjectInputStream
的 readOrdinaryObject()
方法,我们继续进入看源码:
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
if (cl != String.class && cl != Class.class && cl != ObjectStreamClass.class) {
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception var7) {
throw (IOException)(new InvalidClassException(desc.forClass().getName(), "unable to create instance")).initCause(var7);
}
...
}
}
发现调用了 ObjectStreamClass
的 isInstantiable()
方法,而 isInstantiable()
里面的代码如下:
boolean isInstantiable() {
this.requireInitialized();
return this.cons != null;
}
代码非常简单,就是判断一下构造方法是否为空,构造方法不为空就返回 true
,也就是说,只要有无参构造方法就会实例化;这时候,其实还没有找到为什么加上readResolve()
方法就避免了单例被破坏的真正原因,我们再次回到ObjectInputStream
的 readOrdinaryObject()
方法继续往下看可以找到如下代码:
private Object readOrdinaryObject(boolean unshared) throws IOException {
...
if (obj != null && this.handles.lookupException(this.passHandle) == null && desc.hasReadResolveMethod()) {
Object rep = desc.invokeReadResolve(obj);
if (unshared && rep.getClass().isArray()) {
rep = cloneArray(rep);
}
if (rep != obj) {
if (rep != null) {
if (rep.getClass().isArray()) {
this.filterCheck(rep.getClass(), Array.getLength(rep));
} else {
this.filterCheck(rep.getClass(), -1);
}
}
obj = rep;
this.handles.setObject(this.passHandle, rep);
}
}
...
}
判断无参构造方法是否存在之后,又调用了 hasReadResolveMethod()
方法:
boolean hasReadResolveMethod() {
this.requireInitialized();
return this.readResolveMethod != null;
}
逻辑非常简单,就是判断readResolveMethod
是否为空,不为空就返回 true
。那么 readResolveMethod
是在哪里赋值的呢? 通过全局查找找到了赋值代码在私有方法 ObjectStreamClass()
方法中给 readResolveMethod
进行赋值,来看代码:
ObjectStreamClass.this.readResolveMethod = ObjectStreamClass.getInheritableMethod(cl, "readResolve", (Class[])null, Object.class);
代码的逻辑其实就是通过反射找到一个无参的 readResolve()
方法,并且保存下来,现在再回到 ObjectInputStream
的 readOrdinaryObject()
方法继续往下看,如果readResolve()
存在则调用 invokeReadResolve()
方法:
Object invokeReadResolve(Object obj) throws IOException, UnsupportedOperationException {
this.requireInitialized();
if (this.readResolveMethod != null) {
try {
return this.readResolveMethod.invoke(obj, (Object[])null);
} catch (InvocationTargetException var4) {
Throwable th = var4.getTargetException();
if (th instanceof ObjectStreamException) {
throw (ObjectStreamException)th;
} else {
throwMiscException(th);
throw new InternalError(th);
}
} catch (IllegalAccessException var5) {
throw new InternalError(var5);
}
} else {
throw new UnsupportedOperationException();
}
}
我们可以看到在 invokeReadResolve()
方法中用反射调用了 readResolveMethod()
方法。 通过JDK
源码分析我们可以看出,虽然,增加 readResolve()
方法返回实例,解决了单例被破坏的问题。但是,我们通过分析源码以及调试,我们可以看到实际上实例化了两 次,只不过新创建的对象没有被返回而已.
那如果,创建对象的动作发生频率增大,就 意味着内存分配开销也就随之增大;为了解决这个问题,我们推荐使用注册式单例。
为何建议使用注册式(枚举式)单例
我们在前文中说到了,我们极力推荐使用枚举类型的单例;接下来我们分析一下原因:
使用 Java
反编译工具 Jad
(自行下载),解压后,使用命令行调用:
./jad ~/IdeaProjects/own/java-advanced/01.DesignPatterns/design-patterns/build/classes/java/main/com/eamon/javadesignpatterns/singleton/enums/EnumSingleton.class
会在当前目录生成一个 EnumSingleton.jad
文件,我们使用 vscode
打开这个文件查看:
public final class EnumSingleton ext