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("Bob", 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": "Bob",
  "age": 30
}

在 Java 中处理 JSON 最常用的库有:Jackson(最流行)、Gson,以及 MoshiJSON-B 等。

XML——程序员的老朋友

XML(Extensible Markup Language) 更“冗长”,但更加正式、严格。

<User>
  <name>Bob</name>
  <age>30</age>
</User>

在 Java 中处理 XML 常用标准库 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("Bob", 30);
ObjectMapper mapper = new ObjectMapper();

try {
    // 序列化为字符串
    String json = mapper.writeValueAsString(user);
    System.out.println(json); // {"name":"Bob","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