物件序列化是 Java 分散式開發、持久化框架、快取中介軟體的基石。本文從零開始,系統講解序列化/反序列化核心流程、關鍵 API、自訂序列化策略、Externalizable 完全掌控模式,並附完整可執行範例。
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private static final long serialVersionUID = 1L; // 強烈建議顯式宣告
private String username;
private transient String password; // 敏感欄位不序列化
private Date lastLogin;
// 建構子、getter/setter、toString 省略
}
Serializable 為標記介面,無任何方法。JVM 見此介面即啟用預設序列化邏輯。
import java.io.*;
import java.util.Date;
public class SerializeDemo {
public static void main(String[] args) throws IOException {
User user = new User("alice", "secret123", new Date());
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("user.ser"))) {
out.writeObject("UserData"); // 寫入標識字串
out.writeObject(user); // 核心:遞歸序列化物件圖
out.flush();
System.out.println("序列化完成");
}
}
}
public class DeserializeDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
try (ObjectInputStream in = new ObjectInputStream(
new FileInputStream("user.ser"))) {
String tag = (String) in.readObject(); // 須按寫入順序讀取
User user = (User) in.readObject();
System.out.println("Tag: " + tag);
System.out.println("User: " + user);
// password 為 null(transient 跳過)
}
}
}
需私有、特定簽名,JVM 自動回呼。
class SecureUser implements Serializable {
private static final long serialVersionUID = 1L;
private String username;
private transient String encryptedPassword; // 實際儲存加密後密文
private transient String password; // 明文不持久化
// 序列化前:加密並寫入
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 處理非 transient 欄位
// 簡單示範:Base64 編碼模擬加密
String enc = java.util.Base64.getEncoder().encodeToString(password.getBytes());
out.writeObject(enc);
}
// 反序列化後:解密還原
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
String enc = (String) in.readObject();
this.encryptedPassword = enc;
this.password = new String(java.util.Base64.getDecoder().decode(enc));
}
}
鐵律: 必須呼叫 defaultWriteObject()/defaultReadObject();讀寫順序完全一致。
實作 Externalizable,JVM 僅寫入類別身份,所有欄位由開發者手寫。
import java.io.Externalizable;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;
class CustomData implements Externalizable {
private String data;
private int version;
public CustomData() {} // 必須有 public 無參建構子
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(data);
out.writeInt(version);
// 可加入壓縮、加密、版本遷移邏輯
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.data = in.readUTF();
this.version = in.readInt();
// 解壓縮、解密、版本適配
}
}
⚠ 安全提醒: 方法為 public,不可信代碼可竄改流資料。傳輸敏感資訊務必加密簽名。
ObjectOutputStream 維護 handle 表,同一物件僅序列化一次,後續寫入 handle 參考。ObjectStreamClass(含類名、serialVersionUID、欄位描述符)。readObject(若有)。InvalidClassException。務必顯式宣告固定值。transient,必要時自訂加密序列化。readObject 中驗證類別完整性。readObject 相容邏輯;修改欄位型別極高風險,建議新增欄位並棄用舊欄位。Java 原生序列化雖然強大,但需謹慎使用。掌握 Serializable 預設流程、transient 跳過機制、自訂 writeObject/readObject 進階控制、Externalizable 完全自控模式,並理解 handle 表、類別描述、serialVersionUID 等底層機制,是處理物件持久化、分散式通訊、深複製等場景的核心技能。現代架構中,建議優先評估 JSON、Protocol Buffers、Avro 等跨語言、高效能、安全的替代方案。