Java 序列化 (Serialization)
Java 序列化是一种将对象的状态信息转换为可以存储或传输的格式(如字节序列)的机制。反序列化则是从字节序列中重新构建出对象。这个功能对于持久化对象状态或在网络上传输对象非常有用。
什么是序列化?
想象一下,您在程序中创建了一个复杂的对象,它包含了各种状态数据。当程序关闭时,这个对象以及它的状态就会从内存中消失。如果您希望在下次程序启动时能恢复这个对象,就需要将它序列化(写入到文件或数据库),并在需要时反序列化(从文件或数据库中读回)。
Serializable 接口
要使一个类的对象可以被序列化,该类必须实现 java.io.Serializable 接口。
Serializable是一个标记接口 (marker interface),它本身没有任何方法。它只是向 JVM 表明该类的对象是允许被序列化的。
java
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;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}序列化和反序列化对象
ObjectOutputStream: 用于将对象写入到一个输出流(如FileOutputStream)。它的writeObject()方法执行序列化操作。ObjectInputStream: 用于从一个输入流中读取对象。它的readObject()方法执行反序列化操作。
示例代码
java
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
// 1. 创建一个要序列化的对象
User user = new User("Alice", 30);
String filename = "user.ser";
// 2. 序列化对象到文件
try (FileOutputStream fileOut = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
out.writeObject(user);
System.out.println("对象已序列化到 " + filename);
} catch (IOException e) {
e.printStackTrace();
}
// 3. 从文件反序列化对象
User deserializedUser = null;
try (FileInputStream fileIn = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(fileIn)) {
deserializedUser = (User) in.readObject();
System.out.println("\n对象已从 " + filename + " 反序列化");
System.out.println("反序列化后的对象: " + deserializedUser);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}transient 关键字
默认情况下,对象的所有非静态字段都会被序列化。如果您不希望某个字段被序列化(例如,该字段是临时的,或者包含敏感信息如密码),可以使用 transient 关键字来标记它。
java
public class User implements Serializable {
private String username;
private transient String password; // 此字段不会被序列化
// ...
}当一个包含 transient 字段的对象被反序列化时,该字段的值将是其类型的默认值(对于引用类型是 null,对于数值类型是 0,对于布尔类型是 false)。
serialVersionUID
序列化机制通过一个名为 serialVersionUID 的版本号来验证序列化的对象和加载它的类是否兼容。这是一个 private static final long 类型的字段。
- 作用:在反序列化时,JVM 会比较文件中的
serialVersionUID和类中的serialVersionUID。如果两者不匹配,将抛出InvalidClassException。 - 为什么重要:如果您没有显式声明
serialVersionUID,Java 编译器会根据类的结构(字段、方法等)自动生成一个。这意味着如果您修改了类(例如添加或删除了一个字段),自动生成的serialVersionUID就会改变,导致无法反序列化旧版本的对象。
最佳实践是始终在实现了 Serializable 接口的类中显式声明 serialVersionUID。
java
public class User implements Serializable {
// 显式声明 serialVersionUID
private static final long serialVersionUID = 1L;
private String name;
private int age;
// ...
}通过显式声明,即使您对类做了一些不影响序列化兼容性的修改(如添加一个方法),serialVersionUID 保持不变,仍然可以成功反序列化旧对象。