HashMap要本身实现writeObject和readObject方法?

现象 若是你有仔细阅读过HashMap的源码,那么你必定注意过一个问题:HashMap中有两个私有方法。 private void writeObject(java.io.ObjectOutputStream s) throws IOException private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException 这两个方法有两个共同点:java

  1. 都是私有方法数组

  2. 虽然是私有方法,可是在HashMap内部却找不到任何调用它们的地方 疑问ide

  3. 这两个方法是干吗用的?this

  4. 为何要设置成私有的?、 解答对象

  5. HashMap中的writeObject和readObject方法的做用是什么? 答:readObject和writeObject方法都是为了HashMap的序列化而建立的。 首先,HashMap实现了Serializable接口,这意味着该类能够被序列化,而JDK提供的对于Java对象序列化操做的类是ObjectOutputStream,反序列化的类是ObjectInputStream。咱们来看下序列化使用的ObjectOutputStream,它提供了不一样的方法用来序列化不一样类型的对象,好比writeBoolean,wrietInt,writeLong等,对于自定义类型,提供了writeObject方法。 ObjectOutputStream的writeObject方法会调用下面的方法: private void writeSerialData(Object obj, ObjectStreamClass desc) throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; if (slotDesc.hasWriteObjectMethod()) {//若是重写了writeObject方法 PutFieldImpl oldPut = curPut; curPut = null; SerialCallbackContext oldContext = curContext; try { curContext = new SerialCallbackContext(obj, slotDesc); bout.setBlockDataMode(true); slotDesc.invokeWriteObject(obj, this); //调用实现类本身的writeobject方法 bout.setBlockDataMode(false); bout.writeByte(TC_ENDBLOCKDATA); } finally { //省略 } curPut = oldPut; } else { defaultWriteFields(obj, slotDesc); } } } 或者下面这张图也能够: 输入图片说明 能够看到,实际上在ObjectOutputStream中进行序列化操做的时候,会判断被序列化的对象是否本身重写了writeObject方法,若是重写了,就会调用被序列化对象本身的writeObject方法,若是没有重写,才会调用默认的序列化方法。 调用关系以下图:> 这里输入引用文本 输入图片说明继承

  6. 为何HashMap中的readObject和writeObject都是私有的? JDK文档中并无明确说明设置为私有的缘由。方法是私有的,那么该方法没法被子类override,这样作有什么好处呢? 若是我实现了一个继承HashMap的类,我也想有本身的序列化和反序列化方法,那我也能够实现私有的readObject和writeObject方法,而不用关心HashMap本身的那一部分。 下面的部分来自StackOverFlow: We don't want these methods to be overridden by subclasses. Instead, each class can have its own writeObject method, and the serialization engine will call all of them one after the other. This is only possible with private methods (these are not overridden). (The same is valid for readObject.)接口

  7. 为何HashMap要本身实现writeObject和readObject方法,而不是使用JDK统一的默认序列化和反序列化操做呢? 首先要明确序列化的目的,将java对象序列化,必定是为了在某个时刻可以将该对象反序列化,并且通常来说序列化和反序列化所在的机器是不一样的,由于序列化最经常使用的场景就是跨机器的调用,而序列化和反序列化的一个最基本的要求就是,反序列化以后的对象与序列化以前的对象是一致的。 HashMap中,因为Entry的存放位置是根据Key的Hash值来计算,而后存放到数组中的,对于同一个Key,在不一样的JVM实现中计算得出的Hash值多是不一样的。 Hash值不一样致使的结果就是:有可能一个HashMap对象的反序列化结果与序列化以前的结果不一致。即有可能序列化以前,Key='AAA'的元素放在数组的第0个位置,而反序列化值后,根据Key获取元素的时候,可能须要从数组为2的位置来获取,而此时获取到的数据与序列化以前确定是不一样的。 在《Effective Java》中,Joshua大神对此有所解释: 因此为了不这个问题,HashMap采用了下面的方式来解决:图片

  8. 将可能会形成数据不一致的元素使用transient关键字修饰,从而避免JDK中默认序列化方法对该对象的序列化操做。不序列化的包括:Entry[] table,size,modCount。 •文档

  9. 本身实现writeObject方法,从而保证序列化和反序列化结果的一致性。get

那么,HashMap又是经过什么手段来保证序列化和反序列化数据的一致性的呢?

  1. 首先,HashMap序列化的时候不会将保存数据的数组序列化,而是将元素个数以及每一个元素的Key和Value都进行序列化。
  2. 在反序列化的时候,从新计算Key和Value的位置,从新填充一个数组。 想一想看,是否是可以解决序列化和反序列化不一致的状况呢? 因为不序列化存放元素的Entry数组,而是反序列化的时候从新生成,这样就避免了反序列化以后根据Key获取到的元素与序列化以前获取到的元素不一样。
相关文章
相关标签/搜索