什么是序列化反序列化?
- Java序列化是將對象轉換為字節流的過程,因此我們可以將其存儲在磁盤上或通過網絡發送。
- 反序列化是相反的過程–將字節流轉換為內存中的對象。
序列化過程
- 在序列化期間,java運行時將版本號與每個可序列化的類相關聯,此數字稱為 serialVersionUID。
- 在反序列化期間,用於驗證序列化對象的發送者和接收者是否已為該對象加載了與序列化兼容的類。如果接收者為對象加載的類serialVersionUID與相應的發送者的類不同,則反序列化將導致InvalidClassException。
假設一個英國人和另一個印度人都將分別執行序列化和反序列化。在這種情況下, 為了驗證在印度的接收者是經過驗證的人,JVM會創建一個唯一ID,稱為SerialVersionUID。
-
在大多數情況下,序列化和反序列化這兩個活動都是由具有相同系統和相同位置的單個人完成的。
-
但是在序列化中,發送者和接收者不是同一個人,即,這些人可能不同,機器或系統可能不同,並且位置必須不同。在序列化中,發送方和接收方都應僅在開始時具有.class文件,即,要進行序列化的人和准備反序列化的人僅應在開始時包含相同的.class文件。
-
序列化:序列化時,每個對象發送方JVM都會保存一個Unique Identifier。JVM負責根據發送方系統中存在的相應.class文件生成該唯一ID。
-
反序列化:反序列化時,接收方JVM將與對象關聯的唯一ID與本地類Unique ID進行比較,即JVM還將基於接收方系統中存在的相應.class文件創建唯一ID。如果兩個唯一的ID都匹配,則將僅執行反序列化。否則,我們將獲得Runtime Exception,提示InvalidClassException。這個唯一的標識符不過是SerialVersionUID。
SerialVersionUID的生成:
- 就平台和版本而言,發送者和接收者都應使用相同的JVM。否則,由於不同的SerialVersionUID,接收器無法反序列化。
- 發送方和接收方都應使用相同的.class文件版本。序列化后,如果接收方的.class文件中有任何更改,則接收方無法反序列化。
- 為了在內部生成SerialVersionUID,JVM可能使用復雜的算法,這可能會導致性能問題。
示例
- 可序列化的類可以通過聲明一個名為“ serialVersionUID” 的字段來顯式地聲明其自己的serialVersionUID,該字段必須是靜態的,且類型為long。
- 在這里,serialVersionUID表示類的版本,如果您對類的當前版本進行了修改,以使其不再與先前的版本向后兼容,則應該對它進行遞增。
- 因為若不顯式定義 serialVersionUID 的值,Java 會根據類細節自動生成 serialVersionUID 的值,如果對類的源代碼作了修改,再重新編譯,新生成的類文件的serialVersionUID的取值有可能也會發生變化。類的serialVersionUID的默認值完全依賴於Java編譯器的實現,對於同一個類,用不同的Java編譯器編譯,也有可能會導致不同的serialVersionUID,而這會導致問題
//必須實現Serializable接口,不然拋出NotSerializableException
public class DataClass implements Serializable {
// 自定義 SerialVersionUID,可序列化后修改,測試反序列化
private static final long serialVersionUID = 20000L;
String name = "sun";
int age = 21;
}
public class Receiver {
public static void main(String[] args) throws Exception {
// 從 test.txt反序列化
FileInputStream fis = new FileInputStream("test.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
DataClass g = (DataClass)ois.readObject();
System.out.println(g.age);
}
}
public class Sender {
public static void main(String[] args) throws IOException {
DataClass g = new DataClass();
// 序列化到該文件,實際可能是序列化到遠程服務器
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.txt"));
oos.writeObject(g);
oos.close();
System.out.println("全路徑:"+System.getProperty("user.dir")+"test.txt");
}
}