Java序列化和反序列化

Java序列化和反序列化

核心概念

什么是序列化?

对象序列化机制(Object Serialization)是Java语言内建的一种对象持久化机制,它能够:

  • 序列化:将对象的状态转换为字节序列(byte stream)
  • 反序列化:将字节序列重新转换为对象实例
  • 用途:对象存储、网络传输、缓存、深拷贝等

序列化流程图

序列化 存储/传输 读取 反序列化 Java对象 字节序列 文件/网络/缓存 字节序列 Java对象

序列化实现

基本要求

  1. 实现Serializable接口:标记接口,无需实现任何方法
  2. 使用ObjectOutputStream:将对象写入字节流
  3. 处理序列化异常:IOException等

序列化过程

java 复制代码
// 1. 对象实现Serializable接口
// 2. 创建ObjectOutputStream
// 3. 调用writeObject()方法
// 4. 关闭流资源

反序列化实现

基本要求

  1. 类路径可访问:反序列化时类必须在classpath中
  2. 使用ObjectInputStream:从字节流读取对象
  3. 版本兼容性:serialVersionUID必须匹配

反序列化过程

java 复制代码
// 1. 创建ObjectInputStream
// 2. 调用readObject()方法
// 3. 强制类型转换
// 4. 关闭流资源

代码示例

基础序列化示例

java 复制代码
import java.io.*;

/**
 * 用户实体类 - 演示基础序列化
 */
public class User implements Serializable {
    // 序列化版本号
    private static final long serialVersionUID = 1L;
    
    private String username;
    private String email;
    // 敏感信息不序列化
    private transient String password; 
     // 静态变量不序列化
    private static String company = "TechCorp";
    
    public User(String username, String email, String password) {
        this.username = username;
        this.email = email;
        this.password = password;
    }
    
    // getter/setter方法省略...
    
    @Override
    public String toString() {
        return String.format("User{username='%s', email='%s', password='%s', company='%s'}", 
                           username, email, password, company);
    }
}

序列化工具类

java 复制代码
import java.io.*;

/**
 * 序列化工具类 
 */
public class SerializationUtil {
    
    /**
     * 序列化对象到文件
     */
    public static <T> void serializeToFile(T object, String filePath) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(filePath);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(object);
        }
    }
    
    /**
     * 从文件反序列化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserializeFromFile(String filePath) throws IOException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream(filePath);
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            return (T) ois.readObject();
        }
    }
    
    /**
     * 序列化对象到字节数组
     */
    public static <T> byte[] serializeToBytes(T object) throws IOException {
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
             ObjectOutputStream oos = new ObjectOutputStream(baos)) {
            oos.writeObject(object);
            return baos.toByteArray();
        }
    }
    
    /**
     * 从字节数组反序列化对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T deserializeFromBytes(byte[] data) throws IOException, ClassNotFoundException {
        try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
             ObjectInputStream ois = new ObjectInputStream(bais)) {
            return (T) ois.readObject();
        }
    }
    
    /**
     * 深拷贝实现 - 通过序列化
     */
    @SuppressWarnings("unchecked")
    public static <T> T deepCopy(T object) throws IOException, ClassNotFoundException {
        byte[] data = serializeToBytes(object);
        return deserializeFromBytes(data);
    }
}

完整使用示例

java 复制代码
public class SerializationDemo {
    public static void main(String[] args) {
        try {
            // 创建用户对象
            User originalUser = new User("john_doe", "john@example.com", "secret123");
            System.out.println("原始对象: " + originalUser);
            
            // 序列化到文件
            SerializationUtil.serializeToFile(originalUser, "user.ser");
            System.out.println("序列化完成");
            
            // 从文件反序列化
            User deserializedUser = SerializationUtil.deserializeFromFile("user.ser");
            System.out.println("反序列化对象: " + deserializedUser);
            
            // 深拷贝示例
            User copiedUser = SerializationUtil.deepCopy(originalUser);
            System.out.println("深拷贝对象: " + copiedUser);
            
            // 验证是否为不同对象
            System.out.println("是否为同一对象: " + (originalUser == copiedUser));
            System.out.println("内容是否相等: " + originalUser.equals(copiedUser));
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

核心特性详解

1. Serializable接口

  • 标记接口:无方法定义,仅作为序列化标识
  • 必要条件:只有实现此接口的类才能被序列化
  • 继承性:子类自动继承父类的序列化能力

2. transient关键字

java 复制代码
public class SecurityUser implements Serializable {
    private String username;
    // 不会被序列化
    private transient String password;
     // 临时数据不序列化    
    private transient String sessionId;  
    
    // 反序列化后,transient字段会被初始化为默认值
    // String -> null, int -> 0, boolean -> false
}

3. serialVersionUID详解

java 复制代码
public class VersionedClass implements Serializable {
    // 显式声明版本号 
    private static final long serialVersionUID = 1L;
    
    private String name;
    // 后续版本可以安全添加新字段
    // 新增字段,版本号不变
    private String newField; 
}

版本兼容性规则

  • 相同版本号:允许反序列化
  • 不同版本号 :抛出InvalidClassException
  • 未声明版本号:JVM自动生成,类结构变化会导致不兼容

4. 静态变量处理

java 复制代码
public class StaticFieldExample implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String instanceField = "实例字段";
    // 不会被序列化
    private static String staticField = "静态字段"; 
    
    // 静态变量属于类级别,不属于对象状态
    // 反序列化时使用当前类加载时的静态变量值
}

5. 继承关系处理

java 复制代码
// 父类不实现Serializable
class Parent {
    protected String parentField = "parent";
    
    // 必须有无参构造器,反序列化时会调用
    public Parent() {}
}

// 子类实现Serializable
class Child extends Parent implements Serializable {
    private static final long serialVersionUID = 1L;
    private String childField = "child";
    
    // 只有子类字段会被序列化
    // 父类字段在反序列化时通过无参构造器初始化
}

性能优化策略

1. 选择合适的序列化方案

java 复制代码
// 性能对比(仅供参考)
// Java原生序列化:功能完整,性能一般
// JSON序列化:可读性好,跨语言支持
// Protocol Buffers:高性能,体积小
// Kryo:Java专用,性能优秀

public class SerializationBenchmark {
    public static void comparePerformance() {
        // 1. Java原生序列化 - 通用但较慢
        // 2. JSON (Jackson/Gson) - 可读性好
        // 3. Kryo - Java高性能序列化
        // 4. Protocol Buffers - 跨语言高性能
    }
}

2. 减少序列化数据量

java 复制代码
public class OptimizedData implements Serializable {
    private static final long serialVersionUID = 1L;
    
    // 使用transient减少序列化数据
    private String data;
    private transient String derivedData; // 可计算得出的数据
    private transient Object heavyObject; // 大对象不序列化
    
    // 自定义序列化逻辑
    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        // 只序列化核心数据
    }
    
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // 重建派生数据
        this.derivedData = computeDerivedData();
        this.heavyObject = createHeavyObject();
    }
}
相关推荐
zz-zjx3 小时前
Tomcat核心架构与生产部署指南
java·运维·tomcat
灰灰老师3 小时前
在Ubuntu22.04和24.04中安装Docker并安装和配置Java、Mysql、Tomcat
java·mysql·docker·tomcat
二宝1523 小时前
黑马商城day1-MyBatis-Plus
java·开发语言·mybatis
235163 小时前
【MQ】RabbitMQ:架构、工作模式、高可用与流程解析
java·分布式·架构·kafka·rabbitmq·rocketmq·java-rabbitmq
Porunarufu3 小时前
JAVA·类和对象③封装及包
java·开发语言
霍小毛3 小时前
Kubernetes云平台管理实战:滚动升级与秒级回滚
java·容器·kubernetes
代码充电宝3 小时前
LeetCode 算法题【简单】20. 有效的括号
java·算法·leetcode·面试·职场和发展
祈祷苍天赐我java之术4 小时前
Redis 的原子性操作
java·redis
wdfk_prog4 小时前
klist 迭代器初始化:klist_iter_init_node 与 klist_iter_init
java·前端·javascript