java.io.Serializable 是 Java 中的一種標記介面(marker interface)。標記介面是一種特殊的介面,java.io.Serializable 介面沒有任何方法,也沒有常量。
物件序列化是將物件轉換為靜態有序的位元組流的過程,因此該物件可以用於傳輸或持久化。反序列化是該過程的反向操作,即將位元組流轉換為 Java 物件。Java 物件實現 java.io.Serializable 該介面後,就能實現序列化和反序列化。序列化在遠端呼叫中非常常見。
暴露實體
定義 member 實體如下:
@Data
public class Mbr implements java.io.Serializable {
private Long id;
private String name;
private String age;
}
我們在實踐中,可能會遇到不定義 dto 物件,直接在介面中暴露實體物件 create(Mbr mbr)
,update(Mbr mbr)
的情況,這時實體物件必須實現 java.io.Serializable
介面。
當實體實現了 java.io.Serializable
介面後,所有繼承它的實體也能被序列化。如果實體裡引用了其它物件,那麼被引用的物件也應該可以序列化。
@Data
public class Mbr implements java.io.Serializable {
private Long id;
private String name;
private String age;
// Address 應該也實現 java.io.Serializable 介面
private Address address;
}
serialVersionUID
@Data
public class Mbr implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String name;
private String age;
}
JVM 通過版本號(serialVersionUID) 將序列化的實體聯絡起來。在反序列化的過程中,JVM 會將位元組流中的 serialVersionUID 與本地響應的實體類中的 serialVersionUID 比較,相同,可以序列化,否則丟擲 InvaidCastException
。
當實體中不定義 serialVersionUID 欄位時,JVM 將在執行時生成該欄位。所以,這裡的重點是,在開發過程中經常會變更欄位,如果不定義 serialVersionUID 欄位,執行時每次都會生成不同的 serialVersionUID,導致 _InvalidClassExceptions_ 異常。
這種問題還是挺常見的,在微服務 A 中引用另一個微服務 B 的實體(或 dto)包,B 中的實體更新,但沒有更新 A 中的應用,就會引發此問題。