現象 若是你有仔細閱讀過HashMap的源碼,那麼你必定注意過一個問題:HashMap中有兩個私有方法。 private void writeObject(java.io.ObjectOutputStream s) throws IOException private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException 這兩個方法有兩個共同點:java
都是私有方法數組
雖然是私有方法,可是在HashMap內部卻找不到任何調用它們的地方 疑問ide
這兩個方法是幹嗎用的?this
爲何要設置成私有的?、 解答對象
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方法,若是沒有重寫,纔會調用默認的序列化方法。 調用關係以下圖:> 這裏輸入引用文本
繼承
爲何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.)接口
爲何HashMap要本身實現writeObject和readObject方法,而不是使用JDK統一的默認序列化和反序列化操做呢? 首先要明確序列化的目的,將java對象序列化,必定是爲了在某個時刻可以將該對象反序列化,並且通常來說序列化和反序列化所在的機器是不一樣的,由於序列化最經常使用的場景就是跨機器的調用,而序列化和反序列化的一個最基本的要求就是,反序列化以後的對象與序列化以前的對象是一致的。 HashMap中,因爲Entry的存放位置是根據Key的Hash值來計算,而後存放到數組中的,對於同一個Key,在不一樣的JVM實現中計算得出的Hash值多是不一樣的。 Hash值不一樣致使的結果就是:有可能一個HashMap對象的反序列化結果與序列化以前的結果不一致。即有可能序列化以前,Key='AAA'的元素放在數組的第0個位置,而反序列化值後,根據Key獲取元素的時候,可能須要從數組爲2的位置來獲取,而此時獲取到的數據與序列化以前確定是不一樣的。 在《Effective Java》中,Joshua大神對此有所解釋: 因此爲了不這個問題,HashMap採用了下面的方式來解決:圖片
將可能會形成數據不一致的元素使用transient關鍵字修飾,從而避免JDK中默認序列化方法對該對象的序列化操做。不序列化的包括:Entry[] table,size,modCount。 •文檔
本身實現writeObject方法,從而保證序列化和反序列化結果的一致性。get
那麼,HashMap又是經過什麼手段來保證序列化和反序列化數據的一致性的呢?