深入理解java序列化和反序列化

1.定義

  • Java序列化是指把Java對象轉換爲字節序列的過程;
  • Java反序列化是指把字節序列恢復爲Java對象的過程。

對象序列化保存的是對象的”狀態”,即它的成員變量。由此可知,對象序列化不會關注類中的靜態變量。

2. 方法

  • 序列化
  1. 創建一個對象輸出流,它可以包裝一個其它類型的目標輸出流,如文件輸出流:
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\object.out"));
  1. 通過對象輸出流的writeObject()方法寫對象:
oos.writeObject(new User("xuliugen", "123456", "male"));
  • 反序列化
  1. 創建一個對象輸入流,它可以包裝一個其它類型輸入流,如文件輸入流:
ObjectInputStream ois= new ObjectInputStream(new FileInputStream("object.out"));
  1. 通過對象輸出流的readObject()方法讀取對象:
User user = (User) ois.readObject();

3.自定義的序列化和反序列化策略

自定義writeObject 和 readObject 方法(可以允許用戶控制序列化的過程,比如可以在序列化的過程中動態改變序列化的數值)。

  1. java.util.ArrayList類實現了自定義序列化和反序列化,可以查看源碼進行分析【ArrayList實際上是動態數組,每次在放滿以後自動增長設定的長度值,如果數組自動增長長度設爲100,而實際只放了一個元素,那就會序列化99個null元素。爲了保證在序列化的時候不會將這麼多null同時進行序列化,ArrayList把元素數組設置爲transient。】
  2. 自定義writeObject 和 readObject 方法在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法時,會通過反射的方式調用。【可以通過查看源碼writeObject —> writeObject0 —>writeOrdinaryObject—>writeSerialData—>invokeWriteObject。其中writeObjectMethod.invoke(obj, new Object[]{ out });是關鍵】

4. 爲什麼必須實現Serializable

public interface Serializable {
}

查看源碼調用鏈
writeObject —> writeObject0 —>writeOrdinaryObject—>writeSerialData—>invokeWriteObject
writeObject0方法中有這麼一段代碼:

if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "\n" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }

在進行序列化操作時,會判斷要被序列化的類是否是Enum、Array和Serializable類型,如果不是則直接拋出NotSerializableException。

4.使用場景

  • 持久化對象
  • 使用RMI(遠程方法調用)
  • 在網絡中傳遞對象

5. 總結

  1. 實現了java.io.Serializable接口,那麼它就可以被序列化。
  2. 通過ObjectOutputStream和ObjectInputStream對對象進行序列化及反序列化
  3. 虛擬機是否允許反序列化,不僅取決於類路徑和功能代碼是否一致,一個非常重要的一點是兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID)
  4. 聲明爲static和transient類型的成員數據不能被序列化。因爲static代表類的狀態,transient代表對象的臨時數據。
  5. 要想將父類對象也序列化,就需要讓父類也實現Serializable 接口。
  6. Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設爲初始值
  7. 序列化機制的核心作用就是對象狀態的保存與重建。
  8. 如果一個對象的成員變量是一個對象,那麼這個對象的數據成員也會被保存!這是能用序列化解決深拷貝的重要原因
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章