第34篇:Java序列化与反序列化详解

📌 系列导航《Java 100 天进阶之路》完整目录 |
⬅️ 上一篇:第33篇:Java中的static关键字详解 |
➡️ 下一篇:第35篇:Java异常处理最佳实践


一、核心知识点

  • 序列化:将对象转换为字节流,便于存储或网络传输
  • 反序列化:将字节流恢复为 Java 对象
  • Serializable 接口(标记接口,无方法)
  • transient 关键字:跳过序列化
  • serialVersionUID 的作用:版本兼容性
  • 序列化的注意事项(安全、性能、版本)

二、通俗讲解(1分钟开心学)

1. 什么是序列化?

把内存中的 Java 对象变成一串字节(字节流),可以保存到文件、数据库,或者通过网络发送。反序列化就是反过来,把字节流恢复成对象。

2. 如何让一个类可序列化?

实现 java.io.Serializable 接口,它没有任何方法,只是一个“标记”,告诉 JVM:这个类的对象可以被序列化。

3. transient 关键字

如果某个字段不想被序列化(比如密码、缓存数据),可以加上 transient。序列化时会忽略它,反序列化后该字段为默认值(null、0、false)。

4. serialVersionUID

这是一个版本号,用于验证序列化的对象和接收方的类是否兼容。如果没有显式定义,JVM 会根据类结构(字段、方法等)自动生成一个。如果类结构改变(比如增加字段),自动生成的 UID 就会变,导致反序列化旧数据时抛出 InvalidClassException。因此建议手动定义

private static final long serialVersionUID = 1L;

5. 安全性警告

反序列化不可信的数据可能存在安全风险(反序列化漏洞),攻击者可以构造恶意字节流执行任意代码。所以不要反序列化从网络接收的未经验证的数据。

生活类比
序列化就像把一堆积木(对象)拆成零件(字节流),装进盒子。反序列化就是按照说明书把零件重新拼回原样。transient 就像某些零件不放进盒子(如纸条上的密码)。serialVersionUID 就像盒子上写的“拼装图纸版本号”,如果版本不对,就拼不起来。

三、实操代码案例 + 场景说明

场景:保存用户对象到文件,并从中恢复。

import java.io.*;
import java.util.ArrayList;
import java.util.List;

// 可序列化的用户类
class User implements Serializable {
    private static final long serialVersionUID = 1L;  // 版本号,建议显式定义
    
    private String username;
    private transient String password;  // 密码不序列化
    private int age;
    
    public User(String username, String password, int age) {
        this.username = username;
        this.password = password;
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "User{username='" + username + "', password='" + password + "', age=" + age + "}";
    }
}

public class SerializationDemo {
    public static void main(String[] args) throws Exception {
        // 1. 序列化:对象 -> 文件
        User user = new User("张三", "123456", 25);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"));
        oos.writeObject(user);
        oos.close();
        System.out.println("序列化完成");
        
        // 2. 反序列化:文件 -> 对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"));
        User restored = (User) ois.readObject();
        ois.close();
        System.out.println("反序列化得到的对象:" + restored);
        // 输出:password 为 null(transient 导致)
        
        // 3. 序列化多个对象到同一个文件
        List<User> userList = new ArrayList<>();
        userList.add(new User("李四", "pwd1", 30));
        userList.add(new User("王五", "pwd2", 28));
        
        ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream("users.ser"));
        oos2.writeObject(userList);   // 序列化整个集合
        oos2.close();
        
        ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("users.ser"));
        List<User> loadedList = (List<User>) ois2.readObject();
        ois2.close();
        System.out.println("加载的用户列表:" + loadedList);
    }
}

四、避坑要点

错误/误区 后果 正确做法
没有实现 Serializable 就序列化 NotSerializableException 让类实现 Serializable
没有定义 serialVersionUID,然后修改了类结构 反序列化旧数据失败,抛 InvalidClassException 显式定义 serialVersionUID 并谨慎维护
静态变量被序列化 静态变量不属于对象,不会序列化 理解:序列化只保存对象状态,不保存类状态
反序列化不可信数据 安全漏洞(反序列化攻击) 避免反序列化外部数据;使用白名单或替代格式如 JSON

五、面试高频考点

Q1:为什么需要 serialVersionUID

保证序列化和反序列化时的版本一致性。如果不显式定义,JVM 会根据类结构生成哈希值,类稍作改动 UID 就会变,导致旧数据无法反序列化。

Q2:transientstatic 变量会被序列化吗?

static 变量属于类,不序列化;transient 变量在序列化时被忽略,反序列化后为默认值。

Q3:如何避免反序列化漏洞?

  1. 不要反序列化不受信任的数据;2. 使用 ObjectInputStream 的子类并重写 resolveClass 实现白名单;3. 改用 JSON 或 Protobuf 等安全格式。

六、练习题

  1. 设计:编写一个 Employee 类,实现 Serializable,其中 salary 字段不需要序列化,idname 需要。
  2. 动手:将对象序列化到文件,然后修改类(增加一个字段),再反序列化旧文件,观察异常。然后加上 serialVersionUID 再试。
  3. 分析transientstatic 都导致字段不被序列化,但它们的根本区别是什么?

📊 你的学习进度

  • 当前:第34篇 / 共44篇 · 第五阶段:工具类、异常最佳实践、序列化(第32~35篇)
  • ✅ 已完成:第1~33篇
  • 📖 正在学:第34篇
  • ⏳ 待学习:第35~44篇

👉 📚 完整目录 & 学习指南 | 🔥 订阅本专栏,不错过每一篇

💡 本专栏每篇都包含:避坑表 + 面试高频考点 + 练习题。每天30分钟,100天拿offer!


👉 下一篇文章预告

《第35篇:Java异常处理最佳实践》

内容简介:异常处理原则(不吞异常)、try-with-resources、异常链与包装、自定义异常规范、日志记录最佳实践。

💡 学完这篇,你将写出更健壮的异常处理代码,面试再问异常处理轻松回答。

📌 《Java 100 天进阶之路 | 从入门到上岗就业》 每天一篇,建议收藏 + 关注,一起100天拿offer!
👉 点击关注我,更新后第一时间收到推送!
📌 除了Java,我也在深挖智能物流实战(出版社WMS、托盘调度、机器学习落地)。如果你对技术在不同领域的实战感兴趣,欢迎点击我的头像,看看专栏《出版社物流WMS智能调度实战》。技术相通,思路可鉴。

更多推荐