對于 Java 的序列化,我之前一直停留在最淺層次的認知上——把那個要序列化的類實現 Serializbale
接口就可以了嘛。
我似乎不愿意做更深入的研究,因為會用就行了嘛。
但隨著時間的推移,見到 Serializbale
的次數越來越多,我便對它產生了濃厚的興趣。是時候花點時間研究研究了。
01、先來點理論
Java 序列化是 JDK 1.1 時引入的一組開創性的特性,用于將 Java 對象轉換為字節數組,便于存儲或傳輸。此后,仍然可以將字節數組轉換回 Java 對象原有的狀態。
序列化的思想是“凍結”對象狀態,然后寫到磁盤或者在網絡中傳輸;反序列化的思想是“解凍”對象狀態,重新獲得可用的 Java 對象。
序列化有一條規則,就是要序列化的對象必須實現 Serializbale
接口,否則就會報 NotSerializableException 異常。
好,來看看 Serializbale
接口的定義吧:
public interface Serializable {
}
沒別的了!
明明就一個空的接口嘛,竟然能夠保證實現了它的“類對象”被序列化和反序列化?
02、再來點實戰
在回答上述問題之前,我們先來創建一個類(只有兩個字段,和對應的 getter/setter
),用于序列化和反序列化。
class Wanger {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}
再來創建一個測試類,通過 ObjectOutputStream
將“18 歲的王二”寫入到文件當中,實際上就是一種序列化的過程;再通過 ObjectInputStream
將“18 歲的王二”從文件中讀出來,實際上就是一種反序列化的過程。(前面我們學習序列流的時候也講過)
// 初始化
Wanger wanger = new Wanger();
wanger.setName("王二");
wanger.setAge(18);
System.out.println(wanger);// 把對象寫到文件中
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("chenmo"));){oos.writeObject(wanger);
} catch (IOException e) {e.printStackTrace();
}// 從文件中讀出對象
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("chenmo")));){Wanger wanger1 = (Wanger) ois.readObject();System.out.println(wanger1);
} catch (IOException | ClassNotFoundException e) {e.printStackTrace();
}
不過,由于 Wanger
沒有實現 Serializbale
接口,所以在運行測試類的時候會拋出異常,堆棧信息如下:
java.io.NotSerializableException: com.cmower.java_demo.xuliehua.Wangerat java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)at com.cmower.java_demo.xuliehua.Test.main(Test.java:21)
順著堆棧信息,我們來看一下 ObjectOutputStream
的 writeObject0()
方法。其部分源碼如下:
// 判斷對象是否為字符串類型,如果是,則調用 writeString 方法進行序列化
if (obj instanceof String) {writeString((String) obj, unshared);
}
// 判斷對象是否為數組類型,如果是,則調用 writeArray 方法進行序列化
else if (cl.isArray()) {writeArray(obj, desc, unshared);
}
// 判斷對象是否為枚舉類型,如果是,則調用 writeEnum 方法進行序列化
else if (obj instanceof Enum) {writeEnum((Enum<?>) obj, desc, unshared);
}
// 判斷對象是否為可序列化類型,如果是,則調用 writeOrdinaryObject 方法進行序列化
else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);
}
// 如果對象不能被序列化,則拋出 NotSerializableException 異常
else {
if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());
} else {throw new NotSerializableException(cl.getName());
}
}
也就是說,ObjectOutputStream
在序列化的時候,會判斷被序列化的對象是哪一種類型,字符串?數組?枚舉?還是 Serializ