由于,网络传输的数据必须是二进制数据,但调用方请求的出入参数都是对象。对象是不能直接在网络中传输的,所以需要提前把它转成可传输的二进制,并且要求转换算法是可逆的。
- 序列化(serialize):序列化是将对象转换为二进制数据。
- 反序列化(deserialize):反序列化是将二进制数据转换为对象。
JDK序列化#
Serializable 接口#
你的类必须实现 java.io.Serializable 接口。这是一个标记接口(没有任何方法),只告诉 JVM:“这个对象允许被序列化”。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.obj"))
oos.writeObject(user)
1. 序列化到底保存了什么?#
它保存的是对象的“状态”(即成员变量的值),而不保存“方法”(代码逻辑)。
具体流程:
- 序列化时: 通过反射获取对象的所有非静态、非瞬态(
transient)字段,将字段名 + 字段值按特定格式写入字节流,同时会写入类的元信息(如类名、serialVersionUID)。 - 反序列化时: JVM 根据字节流中的类名,去加载对应的
.class文件,然后直接通过内存分配创建对象(不调用构造器!),再把字节流中的字段值赋给这个新对象的对应字段。
2. 核心:serialVersionUID(版本号)#
- JVM 在序列化时会根据类的结构(字段、方法、访问修饰符等)计算出一个哈希值作为
serialVersionUID,并写入字节流。 - 反序列化时,JVM 会对比字节流中的
UID和当前加载的类的UID。如果一致,则允许反序列化;如果不一致,抛出InvalidClassException。
总结:#
JDK 序列化是 Java 内置的对象持久化和传输机制,通过实现 Serializable 标记接口,利用 ObjectOutputStream 和 ObjectInputStream 完成。
它的底层原理是通过反射获取对象的非静态、非瞬态字段,以特定格式写入字节流,并附带类名和 serialVersionUID。反序列化时不走构造器,直接分配内存重建对象。
这里有几个关键点需要注意:static 和 transient 字段不参与序列化;必须显式声明 serialVersionUID 避免版本冲突;如果涉及循环引用,JVM 内部有特殊处理机制。如果对默认方式不满意,还可以通过重写 writeObject 和 readObject 自定义行为。
Externalizable 接口#
public class Employee implements Externalizable {
private String name;
private int salary;
// 必须有无参构造器(public)
public Employee() {}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name); // 手动写
out.writeInt(salary);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF(); // 手动读(顺序必须一致)
salary = in.readInt();
}
}
