既熟悉又陌生的序列化和反序列

既熟悉又陌生的序列化和反序列

什么是序列化和反序列化?

想象一下你要给远方的朋友寄送一个复杂的乐高模型。你不能直接把拼好的模型寄过去,由于运输途中会散架。你需要:

  1. 序列化:把乐高模型拆成一个个零件,按照说明书编号整理(对象 → 字节序列)
  2. 运输:把零件装箱寄出(网络传输或存储到文件)
  3. 反序列化:朋友收到后,按照说明书重新拼装(字节序列 → 对象)

在编程世界中,序列化就是把内存中的对象转换成可以存储或传输的格式,反序列化则是相反的过程。

为什么需要序列化?

主要应用场景:

  1. 网络传输:微服务之间传递对象数据
  2. 数据持久化:将对象保存到文件或数据库
  3. 缓存存储:Redis等缓存系统中的对象存储
  4. 分布式计算:Spark、Hadoop中的数据传输
  5. 会话管理:Web应用中的Session存储

Java 原生序列化机制

基本用法:实现 Serializable 接口

import java.io.*;

// 1. 实现 Serializable 标记接口
public class User implements Serializable {
    // 2. 提议显式声明序列化版本ID
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private transient String password; // transient修饰的字段不会被序列化
    
    // 构造方法、getter、setter...
    public User(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }
    
    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + ", password='" + password + "'}";
    }
}

序列化操作示例

public class SerializationDemo {
    
    // 序列化:对象 -> 字节序列
    public static void serializeUser(User user, String filename) throws IOException {
        try (FileOutputStream fileOut = new FileOutputStream(filename);
             ObjectOutputStream out = new ObjectOutputStream(fileOut)) {
            out.writeObject(user);
            System.out.println("对象序列化完成,保存到: " + filename);
        }
    }
    
    // 反序列化:字节序列 -> 对象
    public static User deserializeUser(String filename) 
            throws IOException, ClassNotFoundException {
        try (FileInputStream fileIn = new FileInputStream(filename);
             ObjectInputStream in = new ObjectInputStream(fileIn)) {
            User user = (User) in.readObject();
            System.out.println("对象反序列化完成");
            return user;
        }
    }
    
    public static void main(String[] args) {
        User originalUser = new User("张三", 25, "secret123");
        
        try {
            // 序列化
            serializeUser(originalUser, "user.ser");
            
            // 反序列化
            User restoredUser = deserializeUser("user.ser");
            
            System.out.println("原始对象: " + originalUser);
            System.out.println("恢复对象: " + restoredUser);
            System.out.println("password字段被transient修饰: " + 
                (restoredUser.toString().contains("null")));
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出结果:

对象序列化完成,保存到: user.ser
对象反序列化完成
原始对象: User{name='张三', age=25, password='secret123'}
恢复对象: User{name='张三', age=25, password='null'}
password字段被transient修饰: true

序列化机制解析

serialVersionUID 的重大性

public class SerialVersionUIDDemo {
    // 场景1:没有显式声明serialVersionUID
    public static class Book implements Serializable {
        private String title;
        private String author;
        // 如果后续添加新字段,反序列化可能失败
    }
    
    // 场景2:显式声明serialVersionUID(推荐)
    public static class SafeBook implements Serializable {
        private static final long serialVersionUID = 123456789L;
        private String title;
        private String author;
        // 即使后续添加字段,只要UID不变,反序列化仍能兼容
        private String isbn; // 新增字段
    }
}

serialVersionUID 的作用:

  • 版本控制:确保序列化和反序列化的类版本一致
  • 兼容性:当类结构变化时,通过控制UID来管理兼容性
  • 安全验证:防止恶意篡改序列化数据

自定义序列化过程

public class CustomSerializationDemo implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String sensitiveData;
    private transient String temporaryData;
    
    // 自定义序列化逻辑
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject(); // 默认序列化
        // 加密敏感数据
        String encrypted = encrypt(sensitiveData);
        oos.writeObject(encrypted);
    }
    
    // 自定义反序列化逻辑
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject(); // 默认反序列化
        // 解密数据
        String encrypted = (String) ois.readObject();
        this.sensitiveData = decrypt(encrypted);
    }
    
    private String encrypt(String data) {
        // 简单的加密演示(实际应用应使用安全算法)
        return Base64.getEncoder().encodeToString(data.getBytes());
    }
    
    private String decrypt(String data) {
        return new String(Base64.getDecoder().decode(data));
    }
}

序列化就像是对象的”语言翻译器”,让对象能在不同的”世界”(网络、文件、进程)中旅行。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
粒粒爱劈叉的头像 - 鹿快
评论 抢沙发

请登录后发表评论

    暂无评论内容