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

Java 序列化與反序列化深度解析:三種實現方式、serialVersionUID、transient 與面試核心考點

     編輯:Java 綜合教學

Java 序列化與反序列化完整指南

1. 核心概念

序列化:將 Java 物件轉換為位元組序列(byte stream)的過程,便於網路傳輸、檔案持久化或跨進程通訊。

反序列化:將位元組序列還原為 Java 物件的逆向過程。

為什麼需要序列化?
網路傳輸、檔案儲存、RPC 呼叫、分散式快取(Redis)、Session 持久化等場景,本質上都只能傳遞二進制資料。序列化是物件與二進制資料之間的橋樑。

2. 三種實現方式詳解

方式一:實現 Serializable 介面 + 預設機制(最常用)

public class Student implements Serializable {
    private static final long serialVersionUID = 3404072173323892464L;
    private String name;
    private transient String id;  // 不參與序列化
    private String age;

    // 建構子、getter/setter、toString()...
}

序列化操作:

// 寫入(序列化)
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.dat"));
oos.writeObject(student);
oos.close();

// 讀取(反序列化)
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.dat"));
Student s = (Student) ois.readObject();
ois.close();

方式二:自定義 writeObject/readObject(控制序列化細節)

在類中私有定義兩個方法,JVM 會自動呼叫:

private void writeObject(ObjectOutputStream oos) throws IOException {
    oos.defaultWriteObject();  // 執行預設序列化(非 transient 欄位)
    oos.writeUTF(id);          // 手動序列化 transient 欄位
}

private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    ois.defaultReadObject();   // 執行預設反序列化
    id = ois.readUTF();        // 手動反序列化 transient 欄位
}

應用場景:加密敏感欄位、自定義序列化格式、處理非 Serializable 的第三方物件。

方式三:實現 Externalizable 介面(完全掌控)

public class Student implements Externalizable {
    private String name;
    private String id;

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(id);  // 必須顯式寫入所有需持久化欄位
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        id = (String) in.readObject();  // 讀取順序必須與寫入順序一致
    }
}

關鍵差異:

  • 不支援方式一的預設機制(writeObject/readObject 不會被呼叫)
  • 必須提供無參建構子(反序列化時需先實例化)
  • 完全由開發者決定序列化邏輯,效能通常優於 Serializable

3. 核心機制深度解析

serialVersionUID:版本控制的關鍵

  • 作用:反序列化時驗證類版本一致性,不匹配拋出 InvalidClassException
  • 隱式生成:未顯式宣告時,JVM 根據類結構(欄位、方法、介面等)自動計算雜湊值
  • 強烈建議顯式宣告:避免跨 JVM、跨編譯器、類結構微調導致的版本不匹配
  • 修改規則:兼容性修改(新增欄位)保持 UID 不變;不兼容修改(刪除欄位、改變型別)需更新 UID

transient 關鍵字:排除敏感/非必要欄位

  • 修飾的欄位不參與預設序列化,反序列化後為預設值(null/0/false)
  • 適用場景:密碼、Session ID、資料庫連線物件、可重新計算的衍生欄位
  • 配合方式二可實現「條件式序列化」或「加密序列化」

序列化過程中的建構子行為

4. 面試高頻考點總結

Q1: 什麼是序列化?如何實現?

參考本文第 1、2 節。重點說明三種方式的適用場景與優劣勢。

Q2: transient 關鍵字作用?如何序列化 transient 欄位?

作用:排除預設序列化。
解決方案:
① 方式二:自定義 writeObject/readObject 手動處理
② 方式三:實現 Externalizable 完全掌控

Q3: 序列化前後物件是否為同一個?

不是同一個物件(記憶體地址不同)。反序列化會建立新物件並填充欄位值。
只能保證:欄位值相等、serialVersionUID 相等、equals() 為 true(若正確重寫)。

Q4: 單例模式與序列化的衝突?

反序列化會破壞單例(產生新實例)。
解決:實現 readResolve() 方法回傳單例實例:

private Object readResolve() {
    return INSTANCE;  // 回傳單例實例,JVM 會用此物件替換新建的物件
}

5. 效能與替代方案

  • Java 原生序列化:通用性強,但體積大、速度慢、不支援跨語言
  • 高效能替代:Protobuf、Kryo、Hessian、FST
  • 跨語言場景:JSON (Jackson/Gson)、MessagePack、Avro

總結:理解三種實現機制的本質差異、掌握 serialVersionUID 與 transient 的正確用法、知道單例破壞與修復方案,即可應對 90% 以上的序列化面試與實戰問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
實現方式反序列化時是否呼叫建構子說明
Serializable否直接分配記憶體並填充欄位,繞過建構子
Externalizable是(無參建構子)先 newInstance() 再呼叫 readExternal()