首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >序列化与反序列化

序列化与反序列化

原创
作者头像
麦克马
修改2025-05-06 16:13:43
修改2025-05-06 16:13:43
2140
举报
文章被收录于专栏:JavaJava

序列化与反序列化

意义

数据持久化:将对象持久化后,可以将其存储到磁盘或数据库中,以便后续读取和恢复对象的状态;

远程通信:在网络传输中,将对象序列化后,可以通过网络传递到远程节点,实现分布式系统间的通信和数据交互;

跨平台传递:通过序列化,可以将对象转换成通用的格式,以便在不同平台、不同语言之间传递和交互;

尝试克隆:通过序列化与反序列化,可以实现对象的尝试克隆,即创建一个与原对象完全相同的新对象。

总结一下,序列化与反序列化就是在对象与特定格式(如二进制、JSON、XML等)之间转换的过程,可以实现数据持久化、数据传输以及跨平台和跨语言通信等功能。不同的序列化方式适用于不同的场景和需求,可以根据具体的情况选择合适的方式进行序列化和反序列化操作。

下面我们以网络传输中的序列化与反序列化场景为例,解释为何要进行序列化和反序列化操作。

网络通信中的序列化与反序列化

TCP 内部传输数据的弊端

我们要思考一个问题, 就是我们在进行套接字编程的时候, tcp协议使用write和read。那么, 我们怎么保证我们写入的和读取的是一个完整的报文呢?

这个问题就类似于管道, 我们也说过,我们的读端和写端存在数据完整性的问题。也类似于文件操作, 我们如何向文件中写入数据, 以及如何保证读取上来的数据是一些完整的数据。所以, 我们如果直接使用套接字编程, 不添加任何协议, 代码就是有bug的。

下面这是一个用户通过tcp协议向远端发送数据,具体的工作流程就是用户向tcp协议的发送缓冲区使用write接口写入数据。然后tcp的缓冲区里面有了这些数据, 就把这些数据发送到远端。

那么, 我们既要知道是tcp管理自己发送缓冲区的数据, 将里面的数据发送到远端。

我们也要知道,网络发送过程中有着各种各样的问题, 比如发多少,什么时候发送, 出错了怎么办, 数据丢包怎么办。因为tcp协议管理发送,所以这些问题都是tcp要解决的问题。

所以, 我们把tcp叫做传输控制协议, 这里重要的就是控制这两个字。比如发多少,什么时候发, 出错了怎么办等等。那么, 我们之前写代码是用read和write从远端读取和发送。但是本质上是将我们用户层要传输的数据拷贝到tcp协议的缓冲区里面。然后发送的问题, 发送中的问题由我们的tcp协议全权负责。上层用户不用管。

而 tcp 是操作系统的一部分,所以我们把数据交给tcp就是把数据交给操作系统。那我们交给操作系统就可以放心了,因为操作系统一定比我们自己实现的更加快速, 安全。

上面是发送, 发送是没有问题的,操作系统帮我们做好了这一切, 但是读取就有问题了。

read是用户从tcp的接收缓冲区里面读取数据, 但是我们此时的接收缓冲区里面可能有对面发送过来的一组数据, 两组数据, 多组数据。又或者把一份整体的数据分成了三份, 但是只发送了一份过来。又或者是已经发送了三四次了, 这些数据在接收缓冲区里面。那么这个时候我们的read可以控制读取多少数据, 是全部读上来呢, 还是读取一半。又或者是读取一部分。

所以,发送端是直接将是数据拷贝给tcp, tcp怎么发是由操作系统决定, 但读端读上来的数据不确定, 那么这个就需要我们在上层应用层定制协议,通过双方定制协议来定制专属的双方能看懂的数据。根据协议将这些读上来的数据进行解析,获得完整的对方想要的数据。

协议

我们规定协议是一种结构化的表示。说是结构化的表示, 其实在开发语言(Java)中就是对象(类)。这个协议中规定了对象包含哪些属性以及属性的类型和依赖关系。但是, 只有这个结构化的数据是不够的。我们总不能把对象在内存中的二进制数据一股脑地都传递给另一方或存储到文件系统中吧?因为我们存储或传输数据时并不太或不需要关心协议本身,也就是说我们并不需要关心对象类的大部分信息,我们只需要知道它是什么类型即可,因为接收方会根据协定好协议去解析为相应的类型。我们其实只关心对象属性,进一步讲是对象属性的值。所以我们仅需要将关心的这些数据通过一定方式组合成一个字节序列,接收方再使用相同的方式相反解析出来,再按照协议映射成为一个对象。

序列化与反序列化

上面提到的按特定方式组合成一个字节序列的过程就是序列化,而通过相同试解析出来的过程就是反序列化。

为什么是字节流?

我们知道计算机中存储(包括磁盘、内存)和网络传输数据都是以二进制的形式进行的,即0和1。但计算机并不是一次只操作一个 bit,而是一个字节,8 bit。所以我们在进行序列化和反序列化的时候也自然而然地以字节为单位。所以为字节流。

Java 中的序列化与反序列化

不同语言中序列化与反序列化的实现方式大都不太一样,即使同一种语言中也会存在多种序列化与反序列化的实现方式。如 JOSN、XML,以及其他第三方库提供的 Hessian、Kyro、Protobut等。他们各自有各自的优缺点,适应不同的应用场景。甚至我们也可以自定义实现符合自己使用的序列化与反序列化器。

Java 中 jdk 自带的序列化方案中,如果我们要将某个对象进行序列化操作,则需要其实现 Serializable 或 ‌Externalizable 接口,前者是个空接口,只作标记用,是 jdk 的默认序列化方式,后者支持使用者对其进行自定义实现,即重写 readObject 和 writeObject 两个方法。

序列化注意事项

  1. 序列化对象必须实现序列化接口。
  2. 序列化对象里面的属性是对象的话也要实现序列化接口。
  3. 类的对象序列化后,类的序列化ID不能轻易修改,不然反序列化会失败。
  4. 类的对象序列化后,类的属性有增加或者删除不会影响序列化,只是值会丢失。
  5. 如果父类序列化了,子类会继承父类的序列化,子类无需添加序列化接口。
  6. 如果父类没有序列化,子类序列化了,子类中的属性能正常序列化,但父类的属性会丢失,不能序列化。
  7. 用Java序列化的二进制字节数据只能由Java反序列化,不能被其他语言反序列化。如果要进行前后端或者不同语言之间的交互一般需要将对象转变成Json/Xml通用格式的数据,再恢复原来的对象。
  8. 如果某个字段不想序列化,在该字段前加上transient关键字即可。

参考:https://mp.weixin.qq.com/s?__biz=Mzg4ODQ1NTE2Mg==&mid=2247571036&idx=1&sn=bcfe1b0d0665daed9de8e86ae7940f3c&chksm=ce0b8c1ac1721a86f9a811cc328affe739c4b50079bab1952448d5608befd10c0b559cdd3991&scene=27

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 序列化与反序列化
    • 意义
    • 网络通信中的序列化与反序列化
      • TCP 内部传输数据的弊端
      • 协议
      • 序列化与反序列化
      • Java 中的序列化与反序列化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档