序列化
序列化是计算机科学中把数据结构或对象的状态转换成可按顺序存储或传输的格式(如字节流、文本)的过程,转换结果可在相同或另一计算环境中还原为与原对象语义相同的副本,这一逆向过程称为反序列化。它使内存中的对象能够脱离程序运行而独立存在,是网络通信、数据持久化和跨语言数据交换的基础机制。
定义
序列化指把程序在运行期持有的数据结构和对象状态编码成线性排列的数据序列,通常表现为字节流或文本字符串,以便写入文件、存入数据库或经由网络发送[1]。当这些字节按原格式重新读取并重建出对象时,这一过程称为反序列化,也叫解编组;两者互为逆操作[2]。
在面向对象的语境下,被转换的内容主要是对象的字段值以及类型标识,而对象所关联的方法并不包含在内[3]。转换后的数据保留了足够的信息,使得接收方可以构造出一个类型相同、状态等价的副本[4]。
这一术语在编程领域还有另一层含义,即多个执行主体对同一资源按次序依次使用,通常译作逐次化,与并行化相对;本词条讨论的是数据编码意义上的序列化[5]。
原理
序列化的基本思路是深度优先地遍历对象图,先输出类型与结构描述,再逐层写入字段的实际取值[4]。反序列化则按同样的规则解析数据,并据此分配和填充对象。若对象中引用了其他对象,默认机制会递归处理被引用的对象;为避免共享引用被重复写出,部分实现会维护一张已序列化对象的记录表,再次遇到同一对象时只写入其编号[6]。
不同格式的编码策略差别很大。文本格式依靠成对的标签或键值对刻画层次,例如 XML 用开始标签与结束标签界定元素,JSON 用花括号与方括号表示对象和数组[7]。二进制格式则通常采用“标签—长度—值”的紧凑结构,只记录字段编号而不重复写字段名,数值部分再用变长整数编码,小整数只占 1 个字节[8]。
一次完整的往返可以抽象为下图所示的关系:$对象 \rightarrow 编码器 \rightarrow 字节序列 \rightarrow 解码器 \rightarrow 对象'$。要保证往返成功,编码端与解码端必须对字段顺序、类型表示、字节序和字符编码达成一致;二进制编码尤其需要注意不同处理器的字节序差异,单纯复制对象在内存中的映像通常无法跨机器读取[1]。
对于需要长期保存或长期演进的数据,实现还会引入版本信息。以 Java 为例,可序列化的类可声明一个名为 serialVersionUID 的静态常量,反序列化时据此判断收发双方的类定义是否兼容,版本号不一致会抛出异常[9]。Python 的 pickle 模块则用协议版本号区分格式,编号越高的协议需要越新的解释器才能读取[2]。
发展历程
序列化的需求与计算机存储和通信的物理特性直接相关。磁盘、磁带和网络链路都按先后次序传递数据,而内存中的对象以随机访问方式分布,因此需要一道把图状结构拉平为线性序列的工序,早期在过程调用和结构化数据交换中已出现类似做法[10]。
在分布式对象技术阶段,序列化被纳入通信框架。公共对象请求代理体系结构(CORBA)的通用对象间协议 GIOP 与 Java 的远程方法调用均采用自定义的二进制编码来传递参数和返回值[1]。Java 平台随后给出了规范化的对象序列化约定,明确了类型与值一并写出、并可由不同版本的类读写兼容数据流的规则[3]。
面向互联网与多语言的格式在这一时期快速发展。XML 成为 SOAP 等协议的基础,2001 年前后出现的二进制消息格式则被用于浏览器与服务器之间的数据交换[1]。谷歌于 2008 年 7 月将 Protocol Buffers 作为开源项目公布,它以 .proto 文件定义结构并生成各语言代码,同一份二进制数据可被不同语言的程序互相解析。
更轻量的文本格式逐渐占据主流。JSON 凭借简洁的语法和浏览器端的原生支持,到 2019 年已成为使用最广的文本编码格式[1]。此后的演进方向主要集中在减少体积和解析开销上,出现了免解析的零拷贝格式以及面向列式分析的存储格式[11]。
应用
数据持久化是最直接的用途。把对象编码后写入文件或数据库,程序下次启动时再解出,对象就能在进程生命周期之外继续存在;缓存系统常把对象转为字节或字符串后存入,读取时再还原[9]。
网络通信是另一类核心场景。远程过程调用需要先把参数和返回值序列化,接收端反序列化后才能使用;跨进程、跨机器传递对象时,所有对象都必须可序列化,否则传输会失败[6]。在分层模型中,这类转换属于应用层的数据表示工作[12]。
跨语言数据交换依赖序列化格式的通用性。Protocol Buffers 的官方实现共用同一种二进制线格式,因而由 Java 程序写出的消息可以直接被 Python 程序解析;其官方文档称典型消息体积约为等价 XML 的 3 至 10 分之一,编解码速度可快 20 至 100 倍[13]。
配置管理与分析型存储同样用到这一机制。部分浏览器与服务器之间的消息采用二进制格式交换,Web 服务的请求与响应则以 XML 或 JSON 描述[1]。面向大数据分析的列式格式会把记录编码后写入存储,以支持大规模读取。
局限
序列化无法完整复现运行时的全部信息。方法、静态成员和被标记为 transient 的实例字段通常不参与序列化,恢复出的对象只是状态上的副本[14]。反序列化只读取对象的数据而不重建类本身,因此对方必须先持有对应类的定义,否则只能得到类型无法识别的数据[6]。
格式差异也带来取舍。文本格式便于阅读与调试,但字段名重复出现导致数据臃肿、解析较慢;二进制格式体积小、速度快,却不可直接阅读,并且需要额外的结构定义文件。二进制编码还需处理字节对齐与字节序问题,简单拷贝内存映像在不同架构之间往往不可用[1]。
结构演进是长期系统中的难点。增删字段可能使旧代码无法读取新数据,或使新代码无法解析旧数据,因此需要设计向前兼容与向后兼容策略;Protocol Buffers 通过不复用字段编号、为已删除字段保留编号来降低风险[11]。若沿用默认机制,Java 序列化中同一可变对象被多次写出时只记录编号,其后的修改不会被反映[6]。
安全方面,反序列化会把外部数据重新变为可执行环境中的对象,这一环节本身构成攻击面。Python 官方文档明确提示,pickle 无法防范构造出的恶意数据在解封时执行任意代码,只应处理可信来源,并建议对不可信数据改用 JSON 等更安全的格式[2]。实现层面通常要求对执行反序列化的代码施加权限限制,例如 Java 要求在自定义序列化方法上具备相应安全权限[15]。
参见
参考资料
- Sérialisation — Wikipédia [引用日期2026-10-29]
- pickle --- Python 对象序列化 [引用日期2026-10-29]
- JavaTM Object Serialization Specification [引用日期2026-10-29]
- 那些鲜为人知的序列化和反序列化底层实现原理! [引用日期2026-10-29]
- シリアライズ [引用日期2026-10-29]
- java序列化,看这篇就够了 [引用日期2026-10-29]
- Marshalling (computer science) [引用日期2026-10-29]
- Protobuf: 高效数据传输的秘密武器 [引用日期2026-10-29]
- 面试官:您能说说序列化和反序列化吗?是怎么实现的?什么场景下需要它? [引用日期2026-10-29]
- Revisions to What is object serialization? [引用日期2026-10-29]
- Data Encoding [引用日期2026-10-29]
- title: Java 序列化详解 [引用日期2026-10-29]
- Introduction to Protocol Buffers [引用日期2026-10-29]
- 对象的序列化与反序列化详解 [引用日期2026-10-29]
- 自定义序列化 [引用日期2026-10-29]