Java 序列化与反序列化
Java 序列化与反序列化
序列化与反序列化的概念
核心定义
- 序列化:将内存中的 Java 对象转换成字节序列(二进制数据)的过程。目的是实现对象的持久化存储(如保存到文件)或网络传输(如跨进程、跨服务器传递对象)。
- 反序列化:将序列化后的字节序列还原为原始 Java 对象的过程。从文件、网络流中读取字节数据,恢复为可操作的对象实例。
核心依赖
要让一个对象支持序列化,其所属的类必须实现 java.io.Serializable 接口。该接口是标记接口(无抽象方法),仅用于告知 JVM 该类对象可被序列化。
序列化的关键要求
实现 Serializable 接口
类声明时显式实现 Serializable,否则序列化时会抛出 NotSerializableException 异常。
序列化版本号(serialVersionUID)
- 须在序列化类中声明静态常量
serialVersionUID(格式:private static final long serialVersionUID = 版本号L;)。 - 作用:用于验证序列化与反序列化时的类结构一致性。若类结构修改(如增减属性、修改方法),但
serialVersionUID不变,反序列化仍可兼容旧版本字节序列;若未声明,JVM 会自动计算,类结构微小变化会导致版本号改变,反序列化失败。 - 生成方式:可手动指定(如
1L),或使用 IDE 插件(如GenerateSerialVersionUID)自动生成唯一值。
![image]()
不参与序列化的属性
- 静态属性:被
static修饰的属性属于类级别的数据,不随对象实例变化,默认不参与序列化。 - ** transient 修饰的非静态属性**:若非静态属性不想被序列化,可添加
transient关键字修饰,序列化时会忽略该属性,反序列化后该属性会恢复为默认值(如null、0)。
完整示例:对象的序列化与反序列化
定义可序列化的实体类(Student)
package com.io.entity;
import java.io.Serializable;
/**
* 可序列化的学生类
* 必须实现 Serializable 接口,并声明 serialVersionUID
* 否则会抛出 InvalidClassException
* transient :不参与序列化
* @author Jing61
*/
public class Student implements Serializable {
// 序列化版本号,确保序列化与反序列化的兼容性
private static final long serialVersionUID = 1L;
private String name;
private int age;
private String sex;
private String address;
private String phone;
// 静态属性:不参与序列化
public static String school = "No.1 Middle School";
// transient 修饰的属性:不参与序列化
private transient String idCard;
public Student(String name, int age, String sex, String address, String phone, String idCard) {
this.name = name;
this.age = age;
this.sex = sex;
this.address = address;
this.phone = phone;
this.idCard = idCard;
}
// getter 和 setter 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
", school='" + school + '\'' + // 静态属性(非序列化字段)
", idCard='" + idCard + '\'' + // transient 属性(非序列化字段)
'}';
}
}
序列化与反序列化工具类(ObjectStream)
使用 ObjectOutputStream 实现序列化(写入字节序列),ObjectInputStream 实现反序列化(读取字节序列)。推荐使用 try-with-resources 自动关闭流资源。
package com.io.io;
import com.io.entity.Student;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 对象序列化与反序列化工具类
*/
public class ObjectStream {
/**
* 序列化:将 Student 对象写入文件(student.dat)
*/
public static void write() {
// 创建待序列化的对象(包含 transient 属性和静态属性)
Student student = new Student("peppa", 18, "Female", "beijing", "123456", "110101200501011234");
// try-with-resources 自动关闭 ObjectOutputStream 和 FileOutputStream
try (var os = new ObjectOutputStream(new FileOutputStream("student.dat"))) {
os.writeObject(student); // 写入对象(序列化核心操作)
System.out.println("序列化成功:对象已写入 student.dat");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反序列化:从文件(student.dat)读取字节序列,还原为 Student 对象
*/
public static void read() {
// try-with-resources 自动关闭 ObjectInputStream 和 FileInputStream
try (var in = new ObjectInputStream(new FileInputStream("student.dat"))) {
Student student = (Student) in.readObject(); // 读取对象(反序列化核心操作)
System.out.println("反序列化成功,还原的对象:");
System.out.println(student.toString());
// 验证:transient 属性 idCard 为 null(未序列化),静态属性 school 为当前类的默认值(非序列化字段)
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
write(); // 执行序列化(先运行一次写入文件)
// read(); // 执行反序列化(读取文件中的对象)
}
}
运行结果与说明
序列化执行结果
- 运行
write()方法后,项目根目录会生成student.dat文件(二进制文件,无法直接打开查看)。
序列化成功:对象已写入 student.dat

反序列化执行结果
- 注释
write(),运行read()方法,控制台输出:
反序列化成功,还原的对象:
Student{name='peppa', age=18, sex='Female', address='beijing', phone='123456', school='No.1 Middle School', idCard='null'}

- 关键结论:
- 常规属性(name、age、sex 等)正常序列化和反序列化;
- 静态属性
school显示当前类的默认值(非序列化字段,未从文件读取); - transient 属性
idCard为null(未参与序列化,反序列化后恢复默认值)。
注意事项
- 序列化类的所有成员必须可序列化:若类中包含其他引用类型属性,该引用类型也必须实现
Serializable接口,否则序列化会失败。 - 避免序列化敏感数据:如密码、身份证号等敏感信息,可使用
transient修饰,或在序列化前加密、脱敏。 - 文件兼容性:序列化后的
.dat文件与serialVersionUID强绑定,类结构修改后需保持版本号一致,否则无法反序列化旧文件。 - 流关闭:
ObjectOutputStream和ObjectInputStream是资源对象,必须关闭(推荐try-with-resources),否则会导致资源泄露。


浙公网安备 33010602011771号