写点什么

序列化单例模式的实现————readResolve 源码解读

用户头像
4ye
关注
发布于: 2 小时前

在可序列化类中加上 readResolve 方法,就可以实现单例模式了!这是为什么呢?让我们一起看看源码中的奥秘吧!


继续上篇单例模式的测试。


只有实现了序列化接口 Serializable ,才可以进行 序列化操作,

测试代码

class SingletonTest {

*/***
** 序列化测试公共方法*
*** *@param* *className*
**/*
private void testSerializable(String className) {
if (className == null) {
throw new RuntimeException("className不能为null");
}
Class<?> clazz = null;
Object obj = null;
try {
clazz = Class.forName(className);
Method method = clazz.getMethod("getInstance");
obj = method.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
int lastIndexOf = className.lastIndexOf(".");
String realName = className.substring(lastIndexOf + 1);
String objName = realName + ".obj";
Object s1 = null;
Object s2 = obj;


FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(objName);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(s2);
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream = new FileInputStream(objName);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Object o = objectInputStream.readObject();
s1 = o;
objectInputStream.close();


System.out.println(s1);
System.out.println(s2);
System.out.println(s1 == s2);




} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}


*/***
** 序列化单例模式l*
**/*
@Test
void testSerializableSingleton() {
testSerializable("com.example.demo.singleton.SerializableSingleton");
}


}
复制代码



序列化单例模式【测试】

先注释掉下面的代码(单例代码在上篇文章有 单例之序列化模式



执行 testSerializableSingleton 方法,结果如下图,序列化和反序列化出来的不是同一个对象,违背了单例模式,也就是说,在这种情况下通过序列化模式可以破坏单例模式.




源码

readObject

接着我们来看看为什么要加上面注释掉的代码,


进入 Object o = objectInputStream.readObject();



进入上面红框中的方法 readObject0,它是 readObject 的底层实现方法,


在该方法中中找到下图


readOrdinaryObject

来到上图中的方法readOrdinaryObject,继续往下看



这里会去判断有没有这个无参构造器,有的话 obj 不为 null,会执行下图中的代码



这里会去判断有没有这个 hasReadResolveMethod ,有的话会通过反射方法


invokeReadResolve去创建这个对象,最后将 obj 的引用地址指向当前创建的对象rep,最后 return 出去。

invokeReadResolve

我们来看看invokeReadResolve做了什么



通过注释可知,它会去调用所表示的可序列化类的 readResolve 方法,在 idea 中通过通过ctrl+鼠标左键点击readResolveMethod方法,选择getInheritableMethod可以看到下图




了解到该方法是一个 参数argTypes为空,返回类型为 Object的函数,那他的修饰符(modifier)是什么呢?



可以知道该方法如果是 static 或者 ABSTRACT 就直接返回 null


抽象方法没有方法体,它需要非抽象子类去实现它,就直接返回null了,


那为什么static也返回null呢!希望看到该博文的大神们帮忙答疑!!🐖谢谢!!


这个我想了好久也想不出答案来。。 直到我重新看到它的方法名 getInheritableMethod:获取可以继承的方法🙃

猜测:

  1. static 方法修饰后它就属于类了,无法被重写,也无法在使用时动态绑定了,如:class A 实现了序列化接口,并定义了readResolve 方法,class B 和 C 都继承了 A ,此时反序列化 B,反序列化的过程会去调用这个invokeReadResolve方法,通过该方法进行反射调用,如果readResolve 方法是static 这时会找不到该方法的。🐷


对猜测进行验证,弄一个简单的继承关系测试下!如图:



输出结果如下图:


**解析:**很明显这里 getDeclaredMethods 是获取不到任何方法的,因为这个只能获取到 B 自己声明的一些方法。


而源码中(上上张图😄)是通过这个getDeclaredMethod 方法去获取的!通过反射的知识点我们知道,该方法只能获取该类自己定义的方法。



其他访问修饰符也是符合对应的权限才会返回该方法的。


最后

欢迎小伙伴们来一起探讨问题~


如果你觉得本篇文章还不错的话,那拜托再点点赞支持一下呀😝

让我们开始这一场意外的相遇吧!~

欢迎留言!谢谢支持!ヾ(≧▽≦*)o 冲冲冲!!

我是 4ye 咱们下期应该……很快再见!! 😆

如果文章对您有所帮助,欢迎关注公众号 J a v a 4 y e 😆


发布于: 2 小时前阅读数: 2
用户头像

4ye

关注

公众号:J a v a 4 y e 2021.07.19 加入

还未添加个人简介

评论

发布
暂无评论
序列化单例模式的实现————readResolve 源码解读