JAVA对象流序列化时的readObject,writeObject,readResolve是怎么被调用的

有时候,我们会在很多涉及到通过JAVA对象流进行序列化和反序列化时,会看到下面的方法:


private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException

private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException


以及我们在写我们的单例类时,如果使用的不是枚举的实现形式,为了保证反序列化出来后的对象,不会破坏单例的情况,我们还会经常看到下面的方法;

private Object readResolve()


大家是否会好奇,为什么这些方法都是private的,并且你会发现这些方法都在他们自身的类中,是没有使用的……那么这些方法到底有什么用呢?我们一起跟踪一下源码看看吧。

因为readObject方法与writeObject方法是大同小异的,我这里仅仅阐述一下readObject方法的大概流程,和几个相关的关键点。

首先,我们看一个很简单的程序代码:

[java]  view plain  copy
  1. public static void main(String[] args) throws Exception {  
  2.         Set<String> set = new HashSet<String>();  
  3.         set.add("11111");  
  4.         set.add("22222");  
  5.         System.out.println(set);  
  6.   
  7.         try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\lianghaohui\\Desktop\\set.obj"))) {  
  8.             oos.writeObject(set);  
  9.         }  
  10.         set.clear();  
  11.         System.out.println(set);  
  12.         try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\lianghaohui\\Desktop\\set.obj"))) {  
  13.             set = (Set<String>) ois.readObject();  
  14.         }  
  15.   
  16.         System.out.println(set);  
  17.     }  


执行结果,也是没什么悬念,将会打印出如下的内容:

[11111, 22222]
[]
[11111, 22222]

但是,如果我们阅读一下HashSet的源码,你将会发现



对的,整个用于保存信息的map(HashSet和LinkedHashSet底层就是用HashMap实现的,CopyOnWriteArraySet底层是用CopyOnWriteArrayList实现的,等等的各种set实现类源码有兴趣可以阅读一下源码)是被用transient关键字修饰了的。


但是,很明显我们的信息是有被序列化成功的,不然反序列化出来时,原本保存在set里面的信息就丢失了。真正的实现的秘密就在于上面提到的readObject方法与writeObject方法了。这里仅简单介绍一下readObject方法,writeObject方法与其类似,不做介绍了。


由于篇幅限制,这里不贴出readObject的源码并一一分析了,它主要做的事情,其实就是读取正常应该被序列化的字段信息后,再构造出一个map,再通过对象流,将原有通过对象流写进文件里面的map信息(容量,每个item信息等)全部读取出来,然后重新构造一个map,这样就使得我们保存在set里面的信息,在经历过对象流的序列化和反序列化后,都没有丢失。那么,这个是private 的 readObject方法是怎么被调用的呢?


简易调用流程图(具体信息,还是需要自己跟踪一下源码了):




看到这里,我们算是大概明白了,为什么诸如HashSet的类里面,要写private 的readObject方法了,因为对象流的读取过程中,它会通过反射的形式,调用private的readObject方法。当然,整个流程走到这里,还是没有走完的,下面还有很多步骤要做,但是因为不属于本篇的讨论范围,这里就不述说了。

但是,我们还有一个疑惑,那就是ObjectStreamClass是什么时候产生的呢?而readObjectMethod这个属性又是怎么得到的呢。

答案就是在ObjectInputStream类的readOrdinaryObject方法调用中:




最后,我们也知道源码里面,是在哪里获得了,我们单例时,所写的那个private 的 readResolve方法的引用了。那么又在整个流程中哪个步骤,通过那个引用,实际调用了我们的readResolve方法呢?答案就是在一开始很前面的ObjectInputStream类的readOrdinaryObject方法,在调用完readSerialData()方法后,就调用了 ObjectStreamClass类的Object invokeReadResolve(Object obj)方法,通过反射调用了我们自己写的readResolve方法,这里不再展开述说了。


本文到此也是要结束了,第一次写源码分析的文章,已经尽力在保持篇幅的情况下,尽量阐述清楚我想要阐述的内容了,也说明白了神奇的三个private 方法:readObject,writeObject,readResolve是在哪里被调用的。

最后,有什么不足之处,望能指出,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值