1. Java 的二進位序列化
二進位序列化是 Java 的標準機制,會將物件轉換為位元組串流,既緊湊又快速。為此會使用類別 ObjectOutputStream 與 ObjectInputStream。產生的檔案是一串位元組,並非供人類閱讀。
之所以稱為二進位,是因為一切都會以「原始」形式序列化:數字、字串、陣列,甚至物件之間的參考都被轉成位元組。這就像把行李箱塞得很緊:效率高、速度快,但沒有說明書時,很難看出東西放在哪裡。
在 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 程式之間傳遞。
缺點
- 不可讀。 人類無法「偷看」內容並理解其中含義。
- 對類別版本高度綁定。 變更結構(新增/刪除欄位)可能導致舊檔案無法讀取。
- 不同版本的 Java 與 JVM 之間可能存在相容性問題。
- 不適合與其他程式語言交換資料。
- 安全性: 從不受信任來源反序列化資料是通往弱點的捷徑。
2. 文字序列化格式:JSON、XML 與其他
二進位序列化適合內部使用,但很多時候需要在不同語言(Java、JavaScript、Python)之間傳遞資料,或以可讀形式保存——便於設定、日誌與 API。此時通常使用文字格式:JSON、XML、YAML、CSV 等。
JSON——最受歡迎
JSON (JavaScript Object Notation) 是一種緊湊且可讀的格式。以下是序列化後的 User 物件範例:
{
"name": "Vasya",
"age": 30
}
在 Java 中,常用的 JSON 函式庫包括:Jackson(最受歡迎)、Gson,還有 Moshi、JSON-B 等。
XML——程式設計師的老朋友
XML (Extensible Markup Language) 較為「冗長」,但格式正式且嚴謹。
<User>
<name>Vasya</name>
<age>30</age>
</User>
對於 XML,在 Java 中經常使用標準函式庫 JAXB(或更早期的 XStream)。
YAML、CSV 與其他
- YAML 與 JSON 相似但更精簡;更常用於設定檔,而非複雜物件的序列化。
- CSV 適合「扁平」表格資料,但不適合巢狀結構。
- 還有其他各式格式,但在 Java 生態中最常用的仍是 JSON 與 XML。
3. 格式比較:何時該用哪一種?
| 格式 | 可讀性 | 緊湊度 | 速度 | 相容性 | 適用情境 |
|---|---|---|---|---|---|
| 二進位 | 否 | ++ | ++ | 僅限 Java | 內部快取、在 JVM 之間快速保存/傳遞 |
| JSON | 是 | + | + | 任何語言 | REST API、與外部服務交換、設定檔 |
| XML | 是 | - | - | 任何語言 | 整合、嚴格結構定義、舊系統 |
- 二進位——用於內部情境,當不需要與外部系統交換且極致效能最重要時。
- JSON——與 web 應用、行動用戶端與 REST API 交換資料,以及存放設定的最佳選擇。
- XML——當需要嚴格的結構定義並與「企業級」解決方案整合時。
重要! 二進位序列化僅適合在 Java 程式之間傳遞資料;即便如此,最好也只在相同版本的程式間使用。文字格式如 JSON 與 XML 更為通用:它們適合跨語言與跨平台交換資料,讓資訊可讀且可攜。
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,需將其函式庫加入專案。我們稍後會學習 Maven 與 Gradle,目前也可手動加入 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 程式間快速讀寫: 使用標準二進位序列化。但請留意版本相容性!
- 與外部服務交換、儲存設定、與前端整合: 使用 JSON(Jackson、Gson)。
- 需要嚴格結構並與「企業級」系統整合: XML(JAXB)。
- 需要讓人能直接開啟並閱讀檔案: 選擇 JSON 或 XML,而非二進位格式。
6. 使用序列化格式的常見錯誤
錯誤 1:嘗試序列化含有不可序列化欄位的物件。 若類別包含未實作 Serializable 的欄位(例如串流或資料庫連線),二進位序列化會拋出錯誤。對 JSON 而言問題較小,但遇到「非標準」型別時也可能出狀況。
錯誤 2:用文字編輯器打開二進位檔後被內容嚇到。 這很正常!二進位檔並非用來給人閱讀的。
錯誤 3:修改了類別結構,舊的二進位檔就無法讀取. 二進位序列化對類別結構變更很敏感——常見 InvalidClassException。在 JSON/XML 中問題較小:未知欄位通常會被忽略或使用預設值。
錯誤 4:用二進位序列化與外部系統交換資料。 這行不通:二進位格式只被 Java 理解,而且還要求版本一致。
錯誤 5:處理 JSON/XML 時忘了加上必要的註解。 有些函式庫需要註解如 @JsonProperty、@XmlElement,否則序列化/反序列化可能不如預期。
錯誤 6:沒有確認所有巢狀物件都可被序列化。 這在二進位序列化中很常見;對 JSON 來說也是,如模型包含複雜型別。
GO TO FULL VERSION