尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
彻底理解序列化与反序列化(基于C/Java)
文章目录1. 序列化与反序列化1.1 数据的“内存格式”1.2 转换过程1.3 “可存储/可传输”的格式2. Java序列化与反序列化3. Java原生序列化与反序列化2.1 Java原生方法调用链2.2 自定义writeObject()和readObject()4. 总结5. 参考1. 序列化与反序列化我们来看一个比较常见的对序列化的定义将内存中的数据结构转换为一种可以存储或传输的格式比如字节流、JSON、XML 等即把数据从内存格式 → 转换为可传输/存储的格式。那么问题来了为什么内存中的数据不能直接传输要做这个转换的过程什么是数据的“内存格式”这个“转换”过程做了什么什么格式是“可存储/可传输”的格式弄清楚这3个概念就彻底掌握了“什么是序列化/反序列化”。1.1 数据的“内存格式”假设以下是一个进程内的地址空间地址0x0019FAC8到0x0019FACB这连续的4个字节代表什么呢答案是不确定。你可能会说这不就是是数值“1”即十六机制的10x00000001。得出这个结论你已经把这台机器的字节序默认为小端序且这是一个整数编码。关于这些硬件底层概念我们就不做过多阐释了提到这些概念是因为它们确确实实影响到了序列化这一过程。很明显直接把上面4字节数据拷贝到另一台机器上不说明它的字节序大端序/小端序、数据编码格式源码/补码/反码机器是无法正确读取并处理的。硬件底层的序列化负责字节序、浮点数标准 、内存对齐这些内容解决“这一串比特这么看”。我们来看一个C语言的结构体structperson{charname[20];// 实际是一个指针指向该数组的首地址intage;// 4字节有符号整数};那么创建一个对象struct person person1 { mingsec,18};它在内存中的结构可能是地址 0x7fff0010: 0x7fff0050 ← 这是个指针指向 name 的实际内容 地址 0x7fff0018: 18 ← age你把这些字节直接发给另一台机器对方看到的就是一堆无意义的数字地址空间隔离内存里的“mingsec”其实是个指针0x7fff0050这个地址只在当前这个进程里有效你把0x7fff0050发给另一台机器对方的内存地址0x7fff0050上可能是系统内核代码或者别人的银行卡密码一读就崩溃段错误。字节序不同大小端问题你用的是 Intel x86 芯片小端序服务器是 IBM Power 芯片大端序。直接把内存里的二进制发过去年龄 18 会被解读成 301989888。所以需要翻译的过程1.2 转换过程基于以上我们的翻译过程就要解决把指针变成数据值把指针找到的 “mingsec” 字面量直接抄进数据包里。把对象类型变成标签告诉对方这是一个 person 结构体把内存布局变成标准格式XML、JSON、Protocol Buffers。1.3 “可存储/可传输”的格式双方约定好的可“正确还原”数据对象的格式都是“可存储/可传输”的格式。常见的几种格式特点JSON文本格式人类可读体积大Protobuf二进制需要先写 .proto 文件定义结构MessagePack二进制类似 JSON 但更紧凑XML文本格式冗长但结构清晰# 内存中的 person1 对象 ↓ 序列化 {name : mingsec, age:18}2. Java序列化与反序列化面向对象的世界中数据封装为类的属性 对数据进行处理的算法或者说逻辑封装为类的方法程序的结构就是对象的调用。我们面向对象开发当然希望对象可以复用所以需要传输或存储对象。程序运行后是存放在内存中的我们在源代码中new Student(mingsec,7)这个类及对象是JVM在负责管理其在内存中的物理结构可能是散乱在各个物理地址空间的01比特流也可能是一段连续的地址空间。JVM抽象了底层的一切使Java程序员可以以“面向对象”这种逻辑视图进行程序开发。要想将内存中的一个对象发送给另一台机器上的JVM进程使用就需要足够的信息对象的类型是什么类对象的状态属性的值附加信息那么我们就需要定义一个数据格式规定提取出来的对象数据是怎样组织的。与之相对应我们需要两个程序读取类元数据、内存中的对象状态等信息输出一个规范格式字节流/字符流序列化读取一个规范格式的字节流/字符流解析输出一个对象反序列化这是一个很自然而然的过程。我们在本地创建对象首先需要有一个类然后通过new关键字调用构造方法给对象赋值。将一个对象序列化就需要告诉对方类名以及属性的值对方就可以通过读取类名加载类并创建对象读取属性值并给创建的对象赋值。3. Java原生序列化与反序列化关于Java中序列化的格式规范官方文档给出了详细说明也在相应的类中定义了格式标记位。想要深入了解规范追着AI问或者自行阅读官方文档。大致流程如下图Java中的对象想要支持序列化其类需要实现Serialiable接口。这个接口只起到标记作用告诉JVM该类的对象是可序列化的。实现序列化和反序列化的功能分别由java.base模块jdk9里的java.io包下的ObjectOutputStream的writeObject()方法和ObjectInputStream的readObject()方法提供下图给出一个序列化的demo.Java序列化的数据以标记AC ED 00 05开头base64编码的特征为rO0AB。我们看一下官方文档协议格式定义给的类源码2.1 Java原生方法调用链序列化主要方法调用链Java 层面 → JVM 本地方法ObjectOutputStream.writeObject(Objectobj)→writeObject0(Objectobj,booleanunshared)→writeOrdinaryObject(Objectobj,ObjectStreamClassdesc,booleanunshared)→writeSerialData(Objectobj,ObjectStreamClassdesc)→DataOutputStream.writeInt(intval)→JVM_WriteInt(C/C实现)// 本地方法写入基本类型反序列化主要方法调用链Java 层面 → JVM 本地方法ObjectInputStream.readObject()→readObject0(booleanunshared)→readOrdinaryObject(booleanunshared)→ObjectStreamClass.newInstance()→JVM_NewInstanceFromConstructor(C/C实现)// 本地方法创建对象→readSerialData(Objectobj,ObjectStreamClassdesc)→Field.set(Objectobj,Objectvalue)→JVM_SetField(C/C实现)// 本地方法设置字段值2.2 自定义writeObject()和readObject()Java支持在类中自定义readObject和writeObject允许开发者细粒度控制序列化和反序列化过程。为什么自定义的 readObject() 或writeObject() 方法会被调用Java 序列化机制的约定Java 的序列化机制在反序列化时会检查目标类是否定义了以下方法privatevoidreadObject(ObjectInputStreamin)throwsIOException,ClassNotFoundException如果该方法存在Java 会通过反射机制调用该方法而不是使用默认的反序列化逻辑。这是 Java 序列化机制的一个约定用于支持类级别的自定义反序列化逻辑。反射机制的调用Java 的 ObjectInputStream 在反序列化一个对象时会执行以下步骤检查目标类是否有自定义的 readObject() 方法。如果有通过反射调用该方法。如果没有则使用默认的反序列化逻辑即逐个读取字段值并赋值给对象。这种机制是通过 ObjectStreamClass 类实现的。ObjectStreamClass 会缓存类的序列化方法信息并在反序列化时调用相应的 invokeReadObject() 方法。4. 总结我们可以把序列化/反序列化拆成两个正交的维度来看第一维硬件底层的“语法序列化”——解决“比特怎么看”负责内容字节序大端/小端、内存对齐Padding、浮点数标准IEEE 754。发生场景任何跨设备传输哪怕是发送一个 int。高级语言的态度封装。Java/Python 的 JVM/解释器已经帮你把 int 转成了固定大端格式的网络字节流程序员在写 writeInt() 时甚至感觉不到字节序的存在——这就是高级语言的好处。第二维语言语义的“结构序列化”——解决“指针对不对”负责内容引用修复Reference Fixup、对象图循环、多态类型还原。为什么基础类型不需要这步 因为基础类型是值语义Value Semantics它是平的而引用类型是指针语义Pointer Semantics它是有向图。内存中的数据不是裸字节而是带有类型、指针、内存布局等语义的结构。这些语义只有当前运行环境能理解。序列化就是把这些环境相关的结构翻译成通用的、可传输的格式。5. 参考[1] Java序列化格式详解[2] 官方文档Java Object Serialization Specification[3] Java安全小白的入门心得 - java反序列化[4] 通义qwen3[5] deepseek[6] mimo
RELATED

相关推荐

巧用python之--模仿PLC(PLC模拟器)

巧用python之--模仿PLC(PLC模拟器)

工作中用到了VM(VisionMaster4.3)有时候需要和PLC打交道,但是PLC毕竟是别人的,不方便修改别人的程序,这时候需要一个灵活的PLC模拟器是多么好呀!先说背景:PLC型号 汇川Easy521: Modbus TCP 192.168.1.10:502 在汇川Easy521中Modbus保持寄存器D寄存器 ,在modbus协议中 0-4区 3区…

📅 2026/9/10 4:57:43
容器集群部署评审,怎样发现隐性风险

容器集群部署评审,怎样发现隐性风险

容器集群部署评审,怎样发现隐性风险 # 生产环境评审中被拦截的错误 YAML 示例 apiVersion: apps/v1 kind: Deployment metadata:name: payment-gateway spec:replicas: 3template:spec:containers:- name: gatewayimage: payment/gateway:v1.4.2resources:requests:…

📅 2026/9/9 16:10:43
终极指南:用 Notepad-- 十六进制编辑破解 5 类二进制文件难题

终极指南:用 Notepad-- 十六进制编辑破解 5 类二进制文件难题

终极指南:用 Notepad-- 十六进制编辑破解 5 类二进制文件难题 【免费下载链接】notepad-- 一个支持windows/linux/mac的文本编辑器,目标是做中国人自己的编辑器,来自中国。 项目地址: https://gitcode.com/GitHub_Trending/no/notepad-- …

📅 2026/10/5 10:37:38
MORE NEWS

更多资讯

📰

Java跨平台原理与企业级开发核心知识体系全解析

1. 从一个“残缺标题”说起:为什么我决定认真聊聊Java看到“java--------------”这个标题,我第一反应是笑了。这明显是个手滑产物,可能是某个同行在搜索框里敲了“java”之后,后面跟了一串无意义的横线,也可能是某个新…

📰

PHP社区源码高仿全开源:LAMP环境搭建与安全加固指南

简介:这是一套高仿亿乐社区的PHP全开源社区系统源码,面向PHP初学者与中小型Web项目开发者,可用于快速搭建类论坛、社交社区或内容聚合平台,帮助理解MVC架构实践、前后端交互及数据库驱动型应用的完整部署流程。资源包共1619个文件…

📰

PaaS化低代码:让低代码真正具备平台级治理能力

1. 从“平台打架”到“能力下沉”:为什么PaaS化的低代码不是概念炒作,而是工程现实的必然选择你有没有遇到过这样的场景:某业务部门急着上线一个客户满意度回访系统,IT部门评估后说“排期三个月,要走需求评审、UI设计、…

📰

小米 MiMo-V2-Omni 实测:全模态 Agent 接入 TaoToken 统一 API 的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

仿银行系统核心账务与避坑实战:从借贷记账到幂等对账

简介:这套仿银行系统是一份基于C#的桌面应用示例代码,面向正在学习WinForms编程、数据库连接或课程设计选题的初学者,演示银行系统常见的界面交互与数据存取流程,能帮助使用者理解一个小型信息系统从界面到数据存储的完整实现。压…

📰

高效春节准备清单:从大扫除到年夜饭的从容安排

要说1月27日这个日子,放在往年我大概率只是翻一眼日历就划过去。但今年不一样,年前休息日排下来,真正能完整用来准备春节的,就数这一天了。于是我干脆把这一天当成一个项目来做,从早上出门采买,到下午打扫布…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬