序列化

序列化:将对象转换为字节流(持久化或传输)。反序列化:字节流还原为对象。

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 深拷贝,更安全。


相关链接