Java 序列化详解
什么是序列化和反序列化?
序列化
序列化是将数据结构或对象转换成一种可以存储或传输的格式。这个格式通常是二进制字节流,但也可以是 JSON、XML 等文本格式。序列化的目的通常是将对象持久化(存储到文件、数据库等),或者在网络上传输。
反序列化
反序列化是序列化的逆过程,即将序列化后的数据重新转换为原始的数据结构或对象。这是为了从存储介质(如文件、数据库等)中恢复数据,或者在网络接收端将数据还原为原始对象。
应用场景
- 网络传输:对象在进行远程调用(如RPC)之前需要序列化,接收到序列化数据后需要进行反序列化。
- 存储文件:将对象持久化到文件中时,先序列化,读取时再反序列化。
- 数据库:将对象存储到数据库中时,通常需要序列化,取出时则需要反序列化。
- 内存存储:将对象缓存到内存时(如 Redis),也需要序列化,取出时需要反序列化。
序列化协议与 TCP/IP 四层模型
在 TCP/IP 协议中,序列化协议属于 应用层。在 OSI 七层模型中,序列化与反序列化的过程类似于表示层的工作,即将应用层的用户数据转换为二进制流并通过网络传输。
常见序列化协议
1. JDK 自带的序列化
JDK 提供了默认的序列化方式,只需实现 java.io.Serializable
接口即可。示例代码如下:
public class RpcRequest implements Serializable {
private static final long serialVersionUID = 1905122041950251207L;
private String requestId;
private String interfaceName;
private String methodName;
private Object[] parameters;
private Class<?>[] paramTypes;
private RpcMessageTypeEnum rpcMessageTypeEnum;
}
serialVersionUID
作用
serialVersionUID
是用于版本控制的标识符。当反序列化时,JVM 会检查序列化对象的 serialVersionUID
是否与当前类的 serialVersionUID
一致。如果不一致,会抛出 InvalidClassException
。
transient
关键字
如果某些字段不需要序列化,可以使用 transient
关键字标记,这样在序列化时这些字段会被忽略,反序列化后它们会被初始化为默认值。
2. Kryo
Kryo 是一种高性能的序列化框架,广泛用于 Java 中。它相比 JDK 自带的序列化方式,具有更小的序列化字节数组和更快的性能。
public class KryoSerializer implements Serializer {
private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
Kryo kryo = new Kryo();
kryo.register(RpcResponse.class);
kryo.register(RpcRequest.class);
return kryo;
});
@Override
public byte[] serialize(Object obj) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream)) {
Kryo kryo = kryoThreadLocal.get();
kryo.writeObject(output, obj);
kryoThreadLocal.remove();
return output.toBytes();
} catch (Exception e) {
throw new SerializeException("Serialization failed");
}
}
@Override
public <T> T deserialize(byte[] bytes, Class<T> clazz) {
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream)) {
Kryo kryo = kryoThreadLocal.get();
Object o = kryo.readObject(input, clazz);
kryoThreadLocal.remove();
return clazz.cast(o);
} catch (Exception e) {
throw new SerializeException("Deserialization failed");
}
}
}
3. Protobuf
Protobuf 是 Google 提供的一种高效的跨平台序列化工具,支持多种语言。Protobuf 需要先定义 .proto
文件,然后通过 IDL 编译器生成对应的代码。
syntax = "proto3";
message Person {
string name = 1;
int32 age = 2;
}
4. ProtoStuff
ProtoStuff 基于 Protobuf 提供了更简单的使用方式,除了支持序列化外,还提供了更多功能,且具有较高的性能。
5. Hessian
Hessian 是一种轻量级的二进制 RPC 协议,支持跨语言序列化。它的优点是较小的序列化体积和良好的跨平台能力。
总结
- JDK 自带序列化:简单易用,但性能较差,且不支持跨语言。
- Kryo:高性能的 Java 序列化框架,适合 Java 内部使用。
- Protobuf/ProtoStuff:适合跨语言使用,支持多种语言,性能优越。
- Hessian:适合跨语言的轻量级 RPC 序列化协议。
根据需求选择合适的序列化协议,可以显著提升应用的性能与扩展性。