CodeGym /課程 /JAVA 25 SELF /標準序列化格式:二進位與文字

標準序列化格式:二進位與文字

JAVA 25 SELF
等級 42 , 課堂 2
開放

1. Java 的二進位序列化

二進位序列化是 Java 的標準機制,會將物件轉換為位元組串流,既緊湊又快速。為此會使用類別 ObjectOutputStreamObjectInputStream。產生的檔案是一串位元組,並非供人類閱讀。

之所以稱為二進位,是因為一切都會以「原始」形式序列化:數字、字串、陣列,甚至物件之間的參考都被轉成位元組。這就像把行李箱塞得很緊:效率高、速度快,但沒有說明書時,很難看出東西放在哪裡。

在 Java 中如何運作?

假設我們有一個類別 User

import java.io.Serializable;

public class User implements Serializable {
    private String name;
    private int age;

    // 建構子、getter 與 setter
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge() { return age; }
}

序列化為二進位檔案

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

User user = new User("Vasya", 30);

try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.bin"))) {
    out.writeObject(user);
    System.out.println("User 物件已序列化到檔案 user.bin");
} catch (Exception e) {
    e.printStackTrace();
}

從二進位檔案反序列化

import java.io.FileInputStream;
import java.io.ObjectInputStream;

try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.bin"))) {
    User loadedUser = (User) in.readObject();
    System.out.println("已從檔案讀取: " + loadedUser.getName() + ", " + loadedUser.getAge());
} catch (Exception e) {
    e.printStackTrace();
}

注意:如果用文字編輯器打開檔案 user.bin,你會看到類似:¬í sr ...——這很正常,設計上就是如此!

二進位序列化的優點

  • 緊湊與速度。 存取極快,沒有多餘的「裝飾」。
  • 會保留物件的所有欄位,包含巢狀物件(前提是它們也可透過 Serializable 序列化)。
  • 適合用於內部快取或在 Java 程式之間傳遞。

缺點

  • 不可讀。 人類無法「偷看」內容並理解其中含義。
  • 對類別版本高度綁定。 變更結構(新增/刪除欄位)可能導致舊檔案無法讀取。
  • 不同版本的 JavaJVM 之間可能存在相容性問題。
  • 不適合與其他程式語言交換資料。
  • 安全性: 從不受信任來源反序列化資料是通往弱點的捷徑。

2. 文字序列化格式:JSON、XML 與其他

二進位序列化適合內部使用,但很多時候需要在不同語言(Java、JavaScript、Python)之間傳遞資料,或以可讀形式保存——便於設定、日誌與 API。此時通常使用文字格式:JSONXMLYAMLCSV 等。

JSON——最受歡迎

JSON (JavaScript Object Notation) 是一種緊湊且可讀的格式。以下是序列化後的 User 物件範例:

{
  "name": "Vasya",
  "age": 30
}

在 Java 中,常用的 JSON 函式庫包括:Jackson(最受歡迎)、Gson,還有 MoshiJSON-B 等。

XML——程式設計師的老朋友

XML (Extensible Markup Language) 較為「冗長」,但格式正式且嚴謹。

<User>
  <name>Vasya</name>
  <age>30</age>
</User>

對於 XML,在 Java 中經常使用標準函式庫 JAXB(或更早期的 XStream)。

YAML、CSV 與其他

  • YAML 與 JSON 相似但更精簡;更常用於設定檔,而非複雜物件的序列化。
  • CSV 適合「扁平」表格資料,但不適合巢狀結構。
  • 還有其他各式格式,但在 Java 生態中最常用的仍是 JSONXML

3. 格式比較:何時該用哪一種?

格式 可讀性 緊湊度 速度 相容性 適用情境
二進位 ++ ++ 僅限 Java 內部快取、在 JVM 之間快速保存/傳遞
JSON + + 任何語言 REST API、與外部服務交換、設定檔
XML - - 任何語言 整合、嚴格結構定義、舊系統
  • 二進位——用於內部情境,當不需要與外部系統交換且極致效能最重要時。
  • JSON——與 web 應用、行動用戶端與 REST API 交換資料,以及存放設定的最佳選擇。
  • XML——當需要嚴格的結構定義並與「企業級」解決方案整合時。

重要! 二進位序列化僅適合在 Java 程式之間傳遞資料;即便如此,最好也只在相同版本的程式間使用。文字格式如 JSONXML 更為通用:它們適合跨語言與跨平台交換資料,讓資訊可讀且可攜。

4. 實作:序列化為二進位與文字格式

二進位序列化(ObjectOutputStream/ObjectInputStream

上面已經看過,這裡再複習一次:

// 序列化
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.bin"))) {
    out.writeObject(user);
}

// 反序列化
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.bin"))) {
    User loadedUser = (User) in.readObject();
}

使用 Jackson 進行 JSON 序列化(簡要)

要使用 Jackson,需將其函式庫加入專案。我們稍後會學習 MavenGradle,目前也可手動加入 JAR 檔。以下為 Maven 依賴範例:

<!-- Maven -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.17.0</version>
</dependency>

序列化/反序列化範例:

import com.fasterxml.jackson.databind.ObjectMapper;

User user = new User("Vasya", 30);
ObjectMapper mapper = new ObjectMapper();

try {
    // 序列化為字串
    String json = mapper.writeValueAsString(user);
    System.out.println(json); // {"name":"Vasya","age":30}

    // 序列化到檔案
    mapper.writeValue(new File("user.json"), user);

    // 從字串反序列化
    User loadedUser = mapper.readValue(json, User.class);

    // 從檔案反序列化
    User loadedFromFile = mapper.readValue(new File("user.json"), User.class);

} catch (Exception e) {
    e.printStackTrace();
}

可以用任何文字編輯器打開 JSON 檔,這讓資料易於閱讀,並能在不同應用與語言之間攜帶與交換。

5. 選擇哪種格式:實用建議

  • 內部快取、暫存檔、在 Java 程式間快速讀寫: 使用標準二進位序列化。但請留意版本相容性!
  • 與外部服務交換、儲存設定、與前端整合: 使用 JSONJacksonGson)。
  • 需要嚴格結構並與「企業級」系統整合: XMLJAXB)。
  • 需要讓人能直接開啟並閱讀檔案: 選擇 JSONXML,而非二進位格式。

6. 使用序列化格式的常見錯誤

錯誤 1:嘗試序列化含有不可序列化欄位的物件。 若類別包含未實作 Serializable 的欄位(例如串流或資料庫連線),二進位序列化會拋出錯誤。對 JSON 而言問題較小,但遇到「非標準」型別時也可能出狀況。

錯誤 2:用文字編輯器打開二進位檔後被內容嚇到。 這很正常!二進位檔並非用來給人閱讀的。

錯誤 3:修改了類別結構,舊的二進位檔就無法讀取. 二進位序列化對類別結構變更很敏感——常見 InvalidClassException。在 JSON/XML 中問題較小:未知欄位通常會被忽略或使用預設值。

錯誤 4:用二進位序列化與外部系統交換資料。 這行不通:二進位格式只被 Java 理解,而且還要求版本一致。

錯誤 5:處理 JSON/XML 時忘了加上必要的註解。 有些函式庫需要註解如 @JsonProperty@XmlElement,否則序列化/反序列化可能不如預期。

錯誤 6:沒有確認所有巢狀物件都可被序列化。 這在二進位序列化中很常見;對 JSON 來說也是,如模型包含複雜型別。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION