Java 中的序列化与反序列化:原理、使用场景及异常处理详解

序列化与反序列化的基本原理

序列化 是将对象的状态转换为字节流的过程,以便保存到文件、数据库或通过网络传输。Java 提供了 java.io.Serializable 接口来实现对象的序列化。任何需要序列化的类都必须实现这个接口。

反序列化 是将字节流转换回对象的过程,恢复对象的状态。Java 提供了 ObjectInputStream 类来读取序列化的对象,并将其转换回原始对象。

序列化的基本步骤
  1. 实现 Serializable 接口 :需要序列化的类必须实现 Serializable 接口。
  2. 使用 ObjectOutputStream:将对象写入输出流。
  3. 使用 ObjectInputStream:从输入流中读取对象。

代码示例

以下代码展示了如何将一个 User 对象序列化到文件中,并从文件中反序列化回来。

复制代码
package chapter08;

import java.io.*;

public class SerializationExample {
    public static void main(String[] args) {
        File dataFile = new File("user.dat");

        // 序列化
        try (ObjectOutputStream objectOut = new ObjectOutputStream(new FileOutputStream(dataFile))) {
            User user = new User("Alice", 25);
            objectOut.writeObject(user);
            System.out.println("序列化后的对象:" + user);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 反序列化
        try (ObjectInputStream objectIn = new ObjectInputStream(new FileInputStream(dataFile))) {
            User user = (User) objectIn.readObject();
            System.out.println("反序列化后的对象:" + user);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + '}';
    }
}
运行结果
复制代码
序列化后的对象:User{name='Alice', age=25}
反序列化后的对象:User{name='Alice', age=25}

异常处理

在进行文件操作和对象序列化时,可能会遇到以下几种异常:

  1. FileNotFoundException:文件未找到异常,通常在尝试打开一个不存在的文件时发生。
  2. IOException:输入输出异常,通常在读写过程中发生。
  3. ClassNotFoundException:类未找到异常,通常在反序列化时发生,表明序列化流中的类在当前环境中不可用。
  4. NotSerializableException :对象未实现 Serializable 接口,无法进行序列化。
异常处理示例
复制代码
package chapter08;

import java.io.*;

public class ExceptionHandlingExample {
    public static void main(String[] args) {
        FileInputStream in = null;
        ObjectInputStream objIn = null;
        ObjectOutputStream objOut = null;

        try {
            in = new FileInputStream("nonexistentfile.txt");

            objOut = new ObjectOutputStream(new FileOutputStream("user.dat"));
            objOut.writeObject(new User("Bob", 30));

            objIn = new ObjectInputStream(new FileInputStream("user.dat"));
            User user = (User) objIn.readObject();
            System.out.println("读取的对象:" + user);

        } catch (FileNotFoundException e) {
            System.err.println("文件未找到:" + e.getMessage());
        } catch (IOException e) {
            System.err.println("输入输出异常:" + e.getMessage());
        } catch (ClassNotFoundException e) {
            System.err.println("类未找到:" + e.getMessage());
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    System.err.println("关闭输入流异常:" + e.getMessage());
                }
            }

            if (objIn != null) {
                try {
                    objIn.close();
                } catch (IOException e) {
                    System.err.println("关闭对象输入流异常:" + e.getMessage());
                }
            }

            if (objOut != null) {
                try {
                    objOut.close();
                } catch (IOException e) {
                    System.err.println("关闭对象输出流异常:" + e.getMessage());
                }
            }
        }
    }
}

class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + '}';
    }
}
运行结果
复制代码
文件未找到:nonexistentfile.txt (系统找不到指定的文件。)
读取的对象:User{name='Bob', age=30}

自定义序列化

有时我们需要自定义序列化过程,Java 提供了 writeObjectreadObject 方法来实现这一点。

复制代码
class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private transient int age; // 不希望序列化的字段使用 transient 关键字

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject(); // 默认序列化
        out.writeInt(age); // 自定义序列化
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject(); // 默认反序列化
        age = in.readInt(); // 自定义反序列化
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + '}';
    }
}

版本控制

序列化的类应定义一个唯一的序列版本号 serialVersionUID,以确保在反序列化时版本的兼容性。

复制代码
class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    // 构造函数、getter、setter 和其他方法
}

使用场景

  1. 持久化存储:将对象保存到文件或数据库中。
  2. 网络传输:在分布式系统中传输对象。
  3. 深拷贝:通过序列化和反序列化实现对象的深拷贝。
实际应用中的注意事项
  1. 安全性:序列化和反序列化可能会带来安全风险,特别是在反序列化不受信任的数据时,可能会引发反序列化漏洞。
  2. 性能:序列化和反序列化的性能可能成为瓶颈,特别是在处理大量数据时。
  3. 兼容性:确保不同版本的类在序列化和反序列化时的兼容性。

总结

  1. 序列化和反序列化:用于将对象转换为字节流以便存储或传输,然后再将字节流转换回对象。
  2. 自定义序列化 :通过实现 writeObjectreadObject 方法来自定义序列化过程。
  3. 版本控制 :使用 serialVersionUID 确保类的版本兼容性。
  4. 异常处理和资源管理:在进行文件操作和对象序列化时,必须处理可能出现的异常,并确保所有打开的流都被正确关闭。
相关推荐
Scott9999HH6 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔6 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社7 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海7 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖8 小时前
JDK 26 新特性详解
java·开发语言
马优晨8 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区10 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大10 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai11 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说11 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json