什么是 Java 序列化?
Java 序列化(Serialization)是一种将 Java 对象的状态(即其成员变量的值)转换为字节流的过程,这个字节流可以持久化到磁盘(如保存为 .ser 文件),也可以通过网络传输到另一台 JVM(Java 虚拟机)上。

与序列化相对应的是反序列化(Deserialization),它将字节流重新恢复成原来的 Java 对象。
核心目的: 实现对象的“持久化存储”和“网络传输”。
如何实现 Java 序列化?(核心步骤)
实现 Java 序列化非常简单,主要遵循以下两个步骤:
让类实现 Serializable 接口
这是最关键的一步,你想要序列化的类,必须实现 java.io.Serializable 接口。

import java.io.Serializable;
// 必须实现 Serializable 接口
public class User implements Serializable {
// ...
}
注意:
Serializable是一个标记接口(Marker Interface),它内部没有任何方法,它就像一个“许可证”,告诉 JVM 这个类的对象可以被序列化。- 如果一个类没有实现
Serializable接口,那么尝试序列化其实例时会抛出NotSerializableException。
使用 ObjectOutputStream 和 ObjectInputStream 进行读写
- 序列化(写入): 使用
ObjectOutputStream将对象写入到输出流(如文件输出流)。 - 反序列化(读取): 使用
ObjectInputStream从输入流(如文件输入流)中读取对象并重建。
完整代码示例
我们创建一个 User 类,然后将其序列化到文件,再从文件中反序列化回来。
User.java (可序列化的类)
import java.io.Serializable;
public class User implements Serializable {
// 序列化版本号,强烈建议添加
private static final long serialVersionUID = 1L;
private String name;
private int age;
// transient 关键字修饰的成员变量不会被序列化
private transient String password;
public User(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", password='" + (password != null ? "******" : "null") + '\'' + // 出于安全考虑,打印时隐藏密码
'}';
}
}
SerializationDemo.java (演示序列化和反序列化)

import java.io.*;
public class SerializationDemo {
public static void main(String[] args) {
// --- 1. 序列化:将对象写入文件 ---
User user = new User("张三", 30, "123456");
String fileName = "user.ser";
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
// writeObject() 方法负责将对象序列化
oos.writeObject(user);
System.out.println("对象序列化成功,已保存到 " + fileName);
} catch (IOException e) {
e.printStackTrace();
}
// --- 2. 反序列化:从文件中读取对象 ---
User deserializedUser = null;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fileName))) {
// readObject() 方法负责从字节流中反序列化对象
deserializedUser = (User) ois.readObject();
System.out.println("对象反序列化成功:");
System.out.println(deserializedUser);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果:
对象序列化成功,已保存到 user.ser
对象反序列化成功:
User{name='张三', age=30, password='null'}
结果分析:
name和age被成功序列化和反序列化。password字段为null,因为它被transient关键字修饰,ObjectOutputStream会自动跳过它。
深入理解:serialVersionUID 和 transient
serialVersionUID (序列化版本号)
serialVersionUID 是一个 private static final long 类型的常量,它的作用是版本控制。
-
为什么需要它? 在反序列化时,JVM 会会检查字节流中的
serialVersionUID与当前类的serialVersionUID是否一致。- 一致: 如果类的定义没有发生破坏性变化(如删除了字段),反序列化成功。
- 不一致: JVM 会认为字节流对应的类版本和当前类版本不兼容,抛出
InvalidClassException。
-
什么时候会不一致? 当你修改了类的结构,
- 增加/删除了一个字段
- 修改了字段的类型
- 修改了类的继承关系
- 等等...
-
最佳实践: 始终为可序列化的类显式声明一个
serialVersionUID。 这样可以让你在类结构发生变化时,主动控制版本兼容性,如果不显式声明,JVM 会根据类的结构自动生成一个,但这种自动生成的方式非常脆弱,任何一个微小的改动(比如修改注释)都可能导致其值改变,从而破坏反序列化。
transient 关键字
transient 关键字用于标记那些不希望被序列化的成员变量。
-
常见使用场景:
- 敏感信息: 如密码、密钥等,这些信息不应该被持久化或通过网络传输。
- 非可序列化对象: 如果一个类的某个成员变量本身是不可序列化的(如
java.io.File对象、数据库连接Connection等),你必须将其标记为transient,否则序列化时会报错。 - 计算得出或可恢复的数据: 某些字段可以通过其他字段计算得出,或者可以在反序列化后重新初始化,那么就没有必要序列化它们。
-
transient字段的默认值:- 对于对象类型:
null - 对于基本数据类型:
0(int),false(boolean) 等
- 对于对象类型:
Java 序列化的优缺点
优点:
- 简单易用: API 设计非常直观,只需实现接口和调用方法即可。
- JVM 内置支持: 无需引入第三方库,是 Java 标准库的一部分。
- 支持对象图: 可以自动处理一个对象内部引用的其他对象(整个对象图)。
缺点:
- 安全性差:
- 反序列化过程可以执行任意代码,存在严重的安全漏洞(如
Apache Commons Collections反序列化漏洞)。 - 敏感数据如果忘记用
transient修饰,会直接暴露。
- 反序列化过程可以执行任意代码,存在严重的安全漏洞(如
- 性能较低: 序列化和反序列化的过程比较耗时,且会产生较大的二进制数据,不适合高性能场景。
- 跨语言性差:
Java序列化是 Java 语言特有的,其他语言(如 Python, C++, Go)无法直接解析.ser文件,这使得它不适用于微服务架构中不同语言服务间的通信。 - 版本兼容性脆弱: 如前所述,类的任何结构变化都可能导致反序列化失败,维护成本高。
现代替代方案
由于 Java 原生序列化的诸多缺点,在现代应用开发中,尤其是在分布式系统和微服务架构中,我们更倾向于使用更通用、更高效、更安全的跨语言序列化方案。
JSON (JavaScript Object Notation)
- 特点: 轻量级、文本格式、易于阅读和编写、跨语言支持最好。
- 常用库:
- Jackson: 性能最好,功能最强大,是 Spring Boot 的默认 JSON 库。
- Gson: Google 出品,API 简单易用。
- Fastjson: 阿里巴巴出品,性能极佳,但历史上曾曝出过安全漏洞,需谨慎使用新版本。
- 适用场景: Web API (RESTful) 响应、配置文件、日志等。
XML (eXtensible Markup Language)
- 特点: 标签化、可扩展性好、格式严谨,但冗余信息较多,比 JSON 更重。
- 常用库:
- JAXB: Java 标准库的一部分,支持注解,可以将 Java 对象直接与 XML 绑定。
- DOM / SAX: 底层的 XML 解析 API
