序列化
序列化:将对象转换为字节流(持久化或传输)。反序列化:字节流还原为对象。
Java 原生序列化
实现 Serializable 接口(标记接口,无方法):
public class User implements Serializable {
// 版本号,反序列化时校验,不一致抛 InvalidClassException
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password; // transient:不参与序列化
private static String company; // static:不参与序列化
}// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.ser"))) {
oos.writeObject(new User("Alice", 30));
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
}serialVersionUID
不显式声明时,JVM 根据类结构自动生成,类结构变化(增删字段)后 ID 变化,导致旧数据无法反序列化。建议始终显式声明。
自定义序列化
实现以下私有方法可自定义序列化行为:
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 先执行默认序列化
oos.writeObject(encrypt(password)); // 再写入加密后的密码
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
this.password = decrypt((String) ois.readObject());
}原生序列化的缺点
- 性能差,序列化结果体积大
- 不跨语言
- 存在安全漏洞(反序列化攻击)
- 不推荐用于分布式系统
JSON 序列化
Jackson
Spring Boot 默认集成,功能最全。
ObjectMapper mapper = new ObjectMapper();
// 序列化
String json = mapper.writeValueAsString(user);
// {"name":"Alice","age":30}
// 反序列化
User user = mapper.readValue(json, User.class);
// List
List<User> users = mapper.readValue(json,
new TypeReference<List<User>>() {});
// 忽略 null 字段
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 忽略未知字段
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);常用注解:
| 注解 | 说明 |
|---|---|
@JsonProperty("user_name") | 指定 JSON 字段名 |
@JsonIgnore | 忽略该字段 |
@JsonIgnoreProperties({"pwd"}) | 忽略多个字段(类级别) |
@JsonFormat(pattern="yyyy-MM-dd") | 日期格式化 |
@JsonInclude(NON_NULL) | null 时不序列化 |
@JsonAlias({"user_name","userName"}) | 反序列化时接受多个名称 |
@JsonSerialize(using=...) | 自定义序列化器 |
@JsonDeserialize(using=...) | 自定义反序列化器 |
Gson
Google 出品,API 简洁。
Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd")
.serializeNulls()
.setPrettyPrinting()
.create();
String json = gson.toJson(user);
User user = gson.fromJson(json, User.class);
List<User> list = gson.fromJson(json, new TypeToken<List<User>>(){}.getType());FastJSON2
阿里出品,高性能。
String json = JSON.toJSONString(user);
User user = JSON.parseObject(json, User.class);
List<User> list = JSON.parseArray(json, User.class);二进制序列化
Protobuf
跨语言,体积小,速度快,需要 .proto 文件定义结构。
message User {
string name = 1;
int32 age = 2;
}User user = User.newBuilder().setName("Alice").setAge(30).build();
byte[] bytes = user.toByteArray();
User deserialized = User.parseFrom(bytes);Kryo
Java 专用,高性能,体积比原生序列化小。
Kryo kryo = new Kryo();
kryo.register(User.class);
Output output = new Output(new FileOutputStream("user.bin"));
kryo.writeObject(output, user);
output.close();
Input input = new Input(new FileInputStream("user.bin"));
User result = kryo.readObject(input, User.class);
input.close();序列化方案对比
| 方案 | 跨语言 | 性能 | 可读性 | 适用场景 |
|---|---|---|---|---|
| Java 原生 | ✗ | 差 | 二进制 | 不推荐 |
| JSON(Jackson) | ✓ | 中 | 好 | REST API、配置 |
| JSON(FastJSON2) | ✓ | 较好 | 好 | 高并发业务 |
| Protobuf | ✓ | 很好 | 二进制 | RPC、存储 |
| Kryo | ✗ | 极好 | 二进制 | Java 内部缓存、MQ |
深拷贝与序列化
序列化可实现深拷贝(对象需 Serializable):
public static <T extends Serializable> T deepCopy(T obj) throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(obj);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
return (T) new ObjectInputStream(bis).readObject();
}实际项目中推荐用 Jackson/Gson 的 readValue(writeValueAsString(obj), Class) 实现 JSON 深拷贝,更安全。