首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 對象序列化基礎與實戰:從 Serializable 到 JSON 最佳實踐

     編輯:Java 相關知識

序列化核心概念

序列化:將對象狀態轉換為字節序列 (存儲/傳輸)
反序列化:從字節序列重建對象

JDK 原生序列化 (Serializable)

實現步驟

  1. 類實現 java.io.Serializable 標記接口
  2. 建議顯式聲明 serialVersionUID (避免版本不兼容 InvalidClassException)
  3. 使用 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,性能更高、版本控制更靈活

原生序列化缺陷與安全風險

  • 不跨語言:二進制格式 Java 專有
  • 體積大:包含類元數據、引用追蹤信息
  • 版本演進困難:增減字段易破壞兼容性
  • 反序列化漏洞 (RCE):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)、跨語言

JSON 序列化最佳實踐 (Jackson)


// 依賴: 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>>() {});

安全加固清單

  1. 禁用 JDK 原生反序列化:JVM 參數 -Djdk.serialFilter=!* (JDK 9+) 或 ObjectInputFilter 白名單
  2. 網絡層面:防火牆限制 RMI/JMX/HTTP 端口暴露
  3. 應用層面:所有外部輸入統一用 JSON/Protobuf,拒絕 ObjectInputStream
  4. 依賴掃描:OWASP Dependency-Check 定期掃描已知漏洞庫

總結

Java 原生序列化雖簡便,但安全風險極高、跨語言無能為力、版本維護痛苦。現代架構應全面遷移至 JSON (Jackson) 或 Protobuf,僅在極少數內部緩存/狀態快照場景謹慎使用 Kryo。序列化技術選型原則:文本優先 (可讀/調試/防火牆友好)、Schema 驅動 (契約優先)、零信任反序列化。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved