Skip to content
清晨的一缕阳光
返回

Java 序列化机制详解

Java 序列化机制详解

序列化是将对象转换为字节流的过程,用于网络传输和持久化存储。

一、序列化基础

1.1 什么是序列化

序列化 = 对象 → 字节流

用途:
- 网络传输(RPC、消息队列)
- 持久化存储(文件、数据库)
- 缓存(Redis、Memcached)
- 深拷贝

1.2 Serializable

// 实现 Serializable 接口
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    
    // 无参构造
    public User() {}
    
    // getter/setter
}

// 序列化
User user = new User("Alice", 20);
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("user.ser"))) {
    oos.writeObject(user);
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("user.ser"))) {
    User deserialized = (User) ois.readObject();
}

1.3 serialVersionUID

// 作用:版本控制
private static final long serialVersionUID = 1L;

// 不指定的后果:
// 类修改后,无法反序列化(InvalidClassException)

// 建议:始终显式声明

二、Externalizable(自定义序列化)

2.1 实现 Externalizable

public class User implements Externalizable {
    private String name;
    private int age;
    
    // 必须有 public 无参构造
    public User() {}
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // 自定义序列化逻辑
        out.writeUTF(name);
        out.writeInt(age);
    }
    
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        // 自定义反序列化逻辑
        name = in.readUTF();
        age = in.readInt();
    }
}

2.2 Serializable vs Externalizable

特性SerializableExternalizable
实现标记接口需实现方法
控制自动手动控制
性能
大小
适用一般场景高性能需求

三、transient 关键字

3.1 忽略字段

public class User implements Serializable {
    private String name;
    private transient String password; // 不序列化
    private transient Connection conn;  // 资源不序列化
    
    // 反序列化后密码为 null
}

3.2 静态字段

public class Config implements Serializable {
    private static final String VERSION = "1.0"; // 不序列化
    private String data; // 序列化
}

四、序列化问题

4.1 单例破坏

// ❌ 序列化破坏单例
public class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

// 反序列化会创建新实例

// ✅ 修复:readResolve
public class Singleton implements Serializable {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    // 反序列化时返回已有实例
    private Object readResolve() {
        return INSTANCE;
    }
}

4.2 继承序列化

// 父类实现 Serializable
public class Parent implements Serializable {
    private String parentField;
}

// 子类自动可序列化
public class Child extends Parent implements Serializable {
    private String childField;
}

// 父类未实现 Serializable
public class Parent { // 未实现
    private String parentField;
}

public class Child extends Parent implements Serializable {
    // 反序列化时,父类字段为默认值
}

4.3 集合序列化

// 集合中的元素必须可序列化
public class Team implements Serializable {
    private List<User> members; // User 必须实现 Serializable
}

五、Protobuf 序列化

5.1 为什么用 Protobuf

Protobuf 优势:
- 体积小(比 Java 序列化小 3-10 倍)
- 速度快(比 Java 序列化快 20-100 倍)
- 跨语言
- 向前/向后兼容

5.2 定义 proto

// user.proto
syntax = "proto3";

message User {
    int32 id = 1;
    string name = 2;
    string email = 3;
    repeated string tags = 4;
}

5.3 生成代码

# 编译 proto
protoc --java_out=. user.proto

5.4 使用示例

// 添加依赖
// <dependency>
//     <groupId>com.google.protobuf</groupId>
//     <artifactId>protobuf-java</artifactId>
//     <version>3.21.7</version>
// </dependency>

// 创建对象
UserProto.User user = UserProto.User.newBuilder()
    .setId(1)
    .setName("Alice")
    .setEmail("alice@example.com")
    .addTags("admin")
    .addTags("user")
    .build();

// 序列化
byte[] data = user.toByteArray();

// 反序列化
UserProto.User parsed = UserProto.User.parseFrom(data);

六、其他序列化协议

6.1 JSON

// Jackson
ObjectMapper mapper = new ObjectMapper();

// 序列化
String json = mapper.writeValueAsString(user);

// 反序列化
User user = mapper.readValue(json, User.class);

6.2 Hessian

// Hessian 序列化
ByteArrayOutputStream baos = new ByteArrayOutputStream();
HessianOutput ho = new HessianOutput(baos);
ho.writeObject(user);

// 反序列化
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
HessianInput hi = new HessianInput(bais);
User user = (User) hi.readObject();

6.3 Kryo

// Kryo 序列化
Kryo kryo = new Kryo();
kryo.register(User.class);

// 序列化
Output output = new Output(new ByteArrayOutputStream());
kryo.writeObject(output, user);
output.close();

// 反序列化
Input input = new Input(new ByteArrayInputStream(output.toBytes()));
User user = kryo.readObject(input, User.class);

七、协议对比

7.1 性能对比

协议大小速度跨语言适用场景
Java 序列化内部系统
ProtobufRPC、存储
JSONWeb API
HessianJava RPC
KryoJava 内部

7.2 选择建议

// ✅ Java 内部、简单场景
Serializable

// ✅ 跨语言、高性能
Protobuf / Thrift

// ✅ Web API
JSON (Jackson / Gson)

// ✅ Java RPC
Hessian / Kryo

// ✅ 大数据
Avro / Parquet

八、最佳实践

8.1 序列化安全

// ✅ 敏感字段用 transient
public class User implements Serializable {
    private String name;
    private transient String password;
}

// ✅ 验证反序列化
private void readObject(ObjectInputStream ois) 
        throws IOException, ClassNotFoundException {
    ois.defaultReadObject();
    // 验证字段
    if (name == null || name.isEmpty()) {
        throw new InvalidObjectException("Invalid name");
    }
}

8.2 版本兼容

// ✅ 使用 serialVersionUID
private static final long serialVersionUID = 1L;

// ✅ 新增字段要有默认值
public class User implements Serializable {
    private String name;
    private String email; // 新增字段,默认 null
    
    // 旧版本序列化的对象可反序列化
}

8.3 性能优化

// ✅ 避免大对象序列化
public class Cache implements Serializable {
    private transient LargeObject cache; // 不序列化
    
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        // 只序列化必要数据
    }
}

// ✅ 使用 Externalizable 自定义
// ✅ 使用 Protobuf 等高效协议

九、总结

序列化核心要点:

协议优点缺点适用场景
Serializable简单、内置慢、大Java 内部
Externalizable可控、高效复杂高性能
Protobuf小、快、跨语言需编译RPC、存储
JSON可读、通用较大Web API

序列化协议选择:跨语言用 Protobuf,Web 用 JSON,Java 内部用 Serializable。


分享这篇文章到:

上一篇文章
Go Map 底层实现原理
下一篇文章
MySQL 分布式事务解决方案