Java 序列化与反序列化终极解析

news2025/4/21 3:28:00

Java 序列化与反序列化终极解析

1. 核心概念

(1) 什么是序列化?

  • 定义:将对象转换为字节流的过程(对象 → 字节)

  • 目的

    • 持久化存储(如保存到文件)

    • 网络传输(如RPC调用)

    • 深拷贝实现

(2) 什么是反序列化?

  • 定义:将字节流还原为对象的过程(字节 → 对象)

  • 关键点

    • 不会调用构造方法

    • 字段值直接从字节流读取

2. 核心API

(1) 序列化相关类

类/接口作用
Serializable标记接口(无方法)
Externalizable提供自定义序列化(需实现2个方法)
ObjectOutputStream序列化输出流
ObjectInputStream反序列化输入流

(2) 关键方法

java

// 序列化
objectOutputStream.writeObject(obj);

// 反序列化
Object obj = objectInputStream.readObject();

3. 完整流程解析

(1) 序列化流程

  1. 检查对象是否实现Serializable

  2. 递归序列化所有非transient字段

  3. 写入类描述信息(含serialVersionUID

  4. 写入字段数据

(2) 反序列化流程

  1. 读取并验证serialVersionUID

  2. 分配对象内存(不调用构造方法)

  3. 递归填充字段值

  4. 对于Externalizable对象,调用readExternal()

4. 代码示例

(1) 基础序列化

java

// 可序列化类
class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private transient int age; // 不参与序列化
    
    // 构造方法、getter/setter省略
}

// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("person.dat"))) {
    oos.writeObject(new Person("Alice", 25));
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("person.dat"))) {
    Person p = (Person) ois.readObject();
    System.out.println(p.getName()); // 输出Alice
    System.out.println(p.getAge()); // 输出0(transient字段)
}

(2) 自定义序列化(Externalizable)

java

class Student implements Externalizable {
    private String name;
    
    // 必须有无参构造器
    public Student() {}
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(name);
    }
    
    @Override
    public void readExternal(ObjectInput in) throws IOException {
        this.name = in.readUTF();
    }
}

5. 关键机制

(1) serialVersionUID

  • 作用:版本控制,防止类结构变更导致反序列化失败

  • 生成方式

    • 显式声明:private static final long serialVersionUID = 1L;

    • 隐式生成:根据类结构计算hash值

(2) 序列化规则

字段类型是否序列化说明
普通实例字段
transient字段如密码字段
static字段属于类而非对象
未实现Serializable抛出异常所有引用字段必须可序列化

6. 高级特性

(1) 自定义序列化

java

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject(); // 默认序列化
    out.writeUTF(encrypt(password)); // 自定义处理
}

private void readObject(ObjectInputStream in) 
    throws IOException, ClassNotFoundException {
    in.defaultReadObject(); // 默认反序列化
    this.password = decrypt(in.readUTF()); // 自定义处理
}

(2) 序列化代理模式

java

private Object writeReplace() {
    return new SerializationProxy(this);
}

private static class SerializationProxy implements Serializable {
    private final String data;
    SerializationProxy(OriginalClass obj) {
        this.data = obj.getData();
    }
    
    private Object readResolve() {
        return new OriginalClass(data);
    }
}

7. 常见问题与解决方案

(1) 反序列化漏洞

  • 风险:攻击者可能构造恶意字节流执行任意代码

  • 防护

    • 使用ObjectInputFilter设置白名单

    java

ObjectInputFilter filter = info -> 
    info.serialClass() == Person.class ? Status.ALLOWED : Status.REJECTED;
ois.setObjectInputFilter(filter);

(2) 性能优化

  • 替代方案

    • JSON(Jackson/Gson)

    • 二进制协议(Protocol Buffers)

    • Kryo(高性能Java序列化)

8. 记忆技巧

(1) 核心口诀

"序列化要接口,transient能跳过
static不算数,UID保平安
构造方法不执行,字段直接写内存"

(2) 流程图解

复制

序列化:
[对象] → [检查Serializable] → [写入元数据] → [递归写字段] → [字节流]

反序列化:
[字节流] → [验证UID] → [分配内存] → [填充字段] → [返回对象]

9. 面试高频问题

  1. Q: 反序列化时如何避免调用构造方法?
    A: JVM直接分配内存并从字节流填充数据

  2. Q: 为什么Serializable是空接口?
    A: 标记接口,仅用于类型检查

  3. Q: 如何实现深拷贝?
    A: 序列化后再反序列化

  4. QExternalizableSerializable区别?
    A: 前者需实现方法,后者自动序列化

10. 最佳实践

  1. 安全性

    • 敏感字段标记transient

    • 使用ObjectInputFilter

  2. 版本控制

    • 始终显式声明serialVersionUID

  3. 性能

    • 避免序列化大对象

    • 考虑替代方案(如ProtoBuf)

  4. 代码健壮性

    • 实现readObject()时保持防御性编程

    • 对不可变对象使用序列化代理模式

通过这个完整体系,你已掌握Java序列化的:
✅ 核心机制 ✅ 安全风险 ✅ 性能优化 ✅ 工程实践

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2338189.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32单片机入门学习——第41节: [12-1] Unix时间戳

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做! 本文写于:2025.04.18 STM32开发板学习——第41节: [12-1] Unix时间戳 前言开发板说明引用解答和科普一…

无人机自主导航与路径规划技术要点!

一、自主导航与路径规划技术要点 1. 传感器融合 GPS/北斗定位:提供全局定位,但在室内或遮挡环境下易失效。 惯性测量单元(IMU)**:通过加速度计和陀螺仪实时追踪姿态,弥补GPS信号丢失时的定位空缺。 …

AI绘画SD中,如何保持生成人物角色脸部一致?Stable Diffusion精准控制AI人像一致性两种实用方法教程!

在AI绘画StableDiffusion中,一直都有一个比较困难的问题,就是如何保证每次出图都是同一个人。今天就这个问题分享一些个人实践,大家和我一起来看看吧。 一. 有哪些实现方式 方式1:固定Seed种子值。 固定Seed种子值出来的图片人…

RK3588S开发板将SPI1接口改成GPIO

参考官方教程:ROC-RK3588S-PC 一.基本知识: 1.GPIO引脚计算: ROC-RK3588S-PC 有 5 组 GPIO bank:GPIO0~GPIO4,每组又以 A0~A7, B0~B7, C0~C7, D0~D7 作为编号区分,常用以下公式计算引脚:GPIO…

PLOS ONE:VR 游戏扫描揭示了 ADHD 儿童独特的大脑活动

在孩子的成长过程中,总有那么一些“与众不同”的孩子。他们似乎总是坐不住,课堂上小动作不断,注意力难以集中,作业总是拖拖拉拉……这些行为常常被家长和老师简单地归结为“淘气”“不听话”。然而,他们可能并不只是“…

DemoGen:用于数据高效视觉运动策略学习的合成演示生成

25年2月来自清华、上海姚期智研究院和上海AI实验室的论文“DemoGen: Synthetic Demonstration Generation for Data-Efficient Visuomotor Policy Learning”。 视觉运动策略在机器人操控中展现出巨大潜力,但通常需要大量人工采集的数据才能有效执行。驱动高数据需…

@JsonView + 单一 DTO:如何实现多场景 JSON 字段动态渲染

JsonView 单一 DTO:如何实现多场景 JSON 字段动态渲染 JsonView 单一 DTO:如何实现多场景 JSON 字段动态渲染1、JsonView 注解产生的背景2、为了满足不同场景下返回对应的属性的做法有哪些?2.1 最快速的实现则是针对不同场景新建不同的 DTO…

15 nginx 中默认的 proxy_buffering 导致基于 http 的流式响应存在 buffer, 以 4kb 一批次返回

前言 这也是最近碰到的一个问题 直连 流式 http 服务, 发现 流式响应正常, 0.1 秒接收到一个响应 但是 经过 nginx 代理一层之后, 就发现了 类似于缓冲的效果, 1秒接收到 10个响应 最终 调试 发现是 nginx 的 proxy_buffering 配置引起的 然后 更新 proxy_buffering 为…

安卓手机万能遥控器APP推荐

软件介绍 安卓手机也能当“家电总控台”?这款小米旗下的万能遥控器APP,直接把遥控器做成“傻瓜式操作”——不用配对,不连蓝牙,点开就能操控电视、空调、机顶盒,甚至其他品牌的电器!雷总这波操作直接封神&…

PH热榜 | 2025-04-18

1. Wiza Monitor 标语:跟踪工作变动,接收Slack和电子邮件的提醒。 介绍:Wiza Monitor是一款用于追踪职位变动的工具,可以实时跟踪客户和潜在客户的工作变动,还可以通过电子邮件和Slack发送提醒,让你的客户…

Android平台 Hal AIDL 系列文章目录

目录 1. Android Hal AIDL 简介2. AIDL 语言简介3. Android 接口定义语言 (AIDL)4. 定义AIDL 接口5. AIDL 中如何传递 Parcelable 对象6. 如何使用AIDL 定义的远程接口进行跨进程通信7. 适用于 HAL 的 AIDL8. Android Hal AIDL 编译调试9. 高版本Android (AIDL HAL) 沿用HIDL方…

十、数据库day02--SQL语句01

文章目录 一、新建查询1.查询窗口的开启方法2. 单语句运行方法 二、数据库操作1.创建数据库2. 使用数据库3. 修改数据库4. 删除数据库和查看所有数据库5. 重点:数据库备份5.1 应用场景5.2 利用工具备份备份操作还原操作 5.3 扩展:使用命令备份 三、数据表…

实时直播弹幕系统设计

整个服务读多写少,读写比例大概几百比1. 如果实时性要求高的话,可以采用长连接模式(轮询的话,时效性不好,同时对于评论少的直播间可能空转) websocket 和 SSE架构 只要求服务端推送的话,可以…

[Java · 初窥门径] Java 语言初识

🌟 想系统化学习 Java 编程?看看这个:[编程基础] Java 学习手册 0x01:Java 编程语言简介 Java 是一种高级计算机编程语言,它是由 Sun Microsystems 公司(已被 Oracle 公司收购)于 1995 年 5 …

【SQL Server】数据探查工具1.0研发可行性方案

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 想抢先解锁数据自由的宝子,速速戳我!评论区蹲一波 “蹲蹲”,揪人唠唠你的超实用需求! 【SQL Server】数据探查工具1.0研发可行性方案…

谓词——C++

1.一元谓词 1.定义 2.案例 查找容器有没有大于五的数字 #include<stdio.h> using namespace std; #include<string> #include<vector> #include<set> #include <iostream> class myfind { public:bool operator()(int a){return a > 5;} …

『前端样式分享』联系我们卡片式布局 自适应屏幕 hover动效 在wikijs中使用 (代码拿来即用)

目录 预览效果分析要点响应式网格布局卡片样式&#xff1a;阴影和过渡效果 代码优化希望 长短不一的邮箱地址在左右居中的同时,做到左侧文字对齐(wikijs可用)总结 欢迎关注 『前端布局样式』 专栏&#xff0c;持续更新中 欢迎关注 『前端布局样式』 专栏&#xff0c;持续更新中…

MySQL 缓存机制全解析:从磁盘 I/O 到性能优化

MySQL 缓存机制全解析&#xff1a;从磁盘 I/O 到性能优化 MySQL 的缓存机制是提升数据库性能的关键部分&#xff0c;它通过多级缓存减少磁盘 I/O 和计算开销&#xff0c;从而提高查询和写入的效率。 1. 为什么需要缓存&#xff1f; 数据库的性能瓶颈通常集中在磁盘 I/O 上。…

1.1 设置电脑开机自动用户登录exe开机自动启动

本文介绍两个事情&#xff1a; 1.Windows如何开机自动登录系统&#xff08;不用输密码) 2. 应用程序(.exe)如何开机自动启动 详细解释如下&#xff1a; 一、Windows如何开机自动登录系统&#xff08;不用输密码) 设备上的工控机&#xff0c;如果开机后都需要操作人员输入密码&…

基于 Python 和 OpenCV 技术的疲劳驾驶检测系统(2.0 全新升级,附源码)

大家好&#xff0c;我是徐师兄&#xff0c;一个有着7年大厂经验的程序员&#xff0c;也是一名热衷于分享干货的技术爱好者。平时我在 CSDN、掘金、华为云、阿里云和 InfoQ 等平台分享我的心得体会。 &#x1f345;文末获取源码联系&#x1f345; 2025年最全的计算机软件毕业设计…