序列化:將對象狀態轉換為字節序列 (存儲/傳輸)
反序列化:從字節序列重建對象
java.io.Serializable 標記接口serialVersionUID (避免版本不兼容 InvalidClassException)ObjectOutputStream.writeObject() / ObjectInputStream.readObject()
@Data // Lombok 生成 getter/setter/toString
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String password; // transient 不參與序列化
private int age;
}
public class SerializationDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
User u = new User("alice", "secret123", 25);
// 序列化到文件
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(u);
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User restored = (User) ois.readObject();
System.out.println(restored); // password 為 null
}
}
}
| 特性 | 說明 |
|---|---|
transient | 字段跳過序列化,反序列化後為默認值 (null/0/false) |
static | 屬於類不屬於實例,自動跳過 |
serialVersionUID | 版本標識,修改類結構必須手動維護,否則 JVM 自動生成 (極不穩定) |
| 繼承 | 父類實現 Serializable → 子類自動可序列化;父類未實現 → 反序列化時調用父類無參構造器 |
| 自定義協議 | 實現 writeObject/readObject 私有方法,完全控制二進制格式 |
Externalizable | 需實現 writeExternal/readExternal,性能更高、版本控制更靈活 |
readObject() 可觸發惡意代碼執行 (Apache Commons Collections、Spring 等歷史漏洞),嚴禁反序列化不可信數據| 方案 | 格式 | 跨語言 | 性能 | 體積 | 安全性 | 適用場景 |
|---|---|---|---|---|---|---|
| JSON (Jackson/Gson) | 文本 | ✅ | 中 | 中 | 高 (無代碼執行) | REST API、配置文件、日誌、跨語言 |
| Protobuf | 二進制 | ✅ | 高 | 小 | 高 | 高性能 RPC、微服務內部通信、存儲 |
| MessagePack | 二進制 | ✅ | 高 | 小 | 高 | 類 JSON 結構、高吞吐場景 |
| Avro | 二進制 | ✅ | 高 | 極小 | 高 | 大數據生態 (Kafka/Hadoop)、Schema Registry |
| Kryo | 二進制 | ❌ | 極高 | 小 | 中 | Java 內部緩存、Spark/Flink 狀態序列化 |
| Hessian | 二進制 | ✅ | 中 | 中 | 中 | 老牌 RPC (Dubbo 1.x)、跨語言 |
// 依賴: com.fasterxml.jackson.core:jackson-databind
ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.registerModule(new JavaTimeModule()); // Java 8 時間類型支援
// 序列化
String json = mapper.writeValueAsString(user);
// 反序列化
User u = mapper.readValue(json, User.class);
// 複雜類型
List<User> list = mapper.readValue(json, new TypeReference<List<User>>() {});
-Djdk.serialFilter=!* (JDK 9+) 或 ObjectInputFilter 白名單ObjectInputStreamJava 原生序列化雖簡便,但安全風險極高、跨語言無能為力、版本維護痛苦。現代架構應全面遷移至 JSON (Jackson) 或 Protobuf,僅在極少數內部緩存/狀態快照場景謹慎使用 Kryo。序列化技術選型原則:文本優先 (可讀/調試/防火牆友好)、Schema 驅動 (契約優先)、零信任反序列化。