序列化单例模式的实现————readResolve 源码解读
在可序列化类中加上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
:获取可以继承的方法🙃
猜测:
- static方法修饰后它就属于类了,无法被重写,也无法在使用时动态绑定了,如:class A 实现了序列化接口,并定义了
readResolve
方法,class B 和 C 都继承了 A ,此时反序列化B,反序列化的过程会去调用这个invokeReadResolve
方法,通过该方法进行反射调用,如果readResolve
方法是static
这时会找不到该方法的。🐷
对猜测进行验证,弄一个简单的继承关系测试下!如图:
输出结果如下图:
解析:很明显这里 getDeclaredMethods
是获取不到任何方法的,因为这个只能获取到 B 自己声明的一些方法。
而源码中(上上张图😄)是通过这个getDeclaredMethod
方法去获取的!通过反射的知识点我们知道,该方法只能获取该类自己定义的方法。
其他访问修饰符也是符合对应的权限才会返回该方法的。
如下表:
修饰符 | 当前类 | 同包 | 子类 | 不同包 |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |