【JAVA基础】java基础之-序列化详解

article/2025/9/14 3:54:27

写在前面的话

脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多。

痛定思痛的我决定从今天开始系统的梳理下知识架构,记录下零散的知识,方便温故知新的同时也顺便清除一些大脑空间用来学习更高深的技术。


目录

前言

序列化和反序列化相关概念

什么是序列化?什么是反序列化?

实际开发中有哪些用到序列化和反序列化的场景?

序列化协议对应于 TCP/IP 4 层模型的哪一层?

常见序列化协议对比

JDK 自带的序列化方式

Kryo

Protobuf

ProtoStuff

hessian

总结

其他推荐阅读


前言

序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。序列化是java实现I/O的重要技术基础。

序列化和反序列化相关概念

什么是序列化?什么是反序列化?

如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。

简单来说:

  • 序列化: 将数据结构或对象转换成二进制字节流的过程
  • 反序列化:将在序列化过程中所生成的二进制字节流的过程转换成数据结构或者对象的过程

对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。

维基百科是如是介绍序列化的:

序列化(serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。

综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

f77bf5582ca0ce189eed9489d2182174.png

实际开发中有哪些用到序列化和反序列化的场景?

  1. 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
  2. 将对象存储到文件中的时候需要进行序列化,将对象从文件中读取出来需要进行反序列化。
  3. 将对象存储到缓存数据库(如 Redis)时需要用到序列化,将对象从缓存数据库中读取出来需要反序列化。

序列化协议对应于 TCP/IP 4 层模型的哪一层?

我们知道网络通信的双方必须要采用和遵守相同的协议。TCP/IP 四层模型是下面这样的,序列化协议属于哪一层呢?

  1. 应用层
  2. 传输层
  3. 网络层
  4. 网络接口层

ac00a75fe94a6779b5924bc2e545c06b.png

如上图所示,OSI 七层协议模型中,表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据。这不就对应的是序列化和反序列化么?

因为,OSI 七层协议模型中的应用层、表示层和会话层对应的都是 TCP/IP 四层模型中的应用层,所以序列化协议属于 TCP/IP 协议应用层的一部分。

常见序列化协议对比

JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且部分版本有安全漏洞。比较常用的序列化协议有 hessian、kyro、protostuff。

下面提到的都是基于二进制的序列化协议,像 JSON 和 XML 这种属于文本类序列化方式。虽然 JSON 和 XML 可读性比较好,但是性能较差,一般不会选择。

JDK 自带的序列化方式

JDK 自带的序列化,只需实现 java.io.Serializable接口即可。

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@ToString
public class RpcRequest implements Serializable {private static final long serialVersionUID = 1905122041950251207L;private String requestId;private String interfaceName;private String methodName;private Object[] parameters;private Class<?>[] paramTypes;private RpcMessageTypeEnum rpcMessageTypeEnum;
}

序列化号 serialVersionUID 属于版本控制的作用。序列化的时候 serialVersionUID 也会被写入二级制序列,当反序列化时会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致。如果 serialVersionUID 不一致则会抛出 InvalidClassException 异常。强烈推荐每个序列化类都手动指定其 serialVersionUID,如果不手动指定,那么编译器会动态生成默认的序列化号

我们很少或者说几乎不会直接使用这个序列化方式,主要原因有两个:

  1. 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
  2. 性能差 :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。

Kryo

Kryo 是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的字节码体积。

另外,Kryo 已经是一种非常成熟的序列化实现了,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛的使用。

guide-rpc-frameworkopen in new window 就是使用的 kyro 进行序列化,序列化和反序列化相关的代码如下:

/*** Kryo serialization class, Kryo serialization efficiency is very high, but only compatible with Java language** @author shuang.kou* @createTime 2020年05月13日 19:29:00*/
@Slf4j
public class KryoSerializer implements Serializer {/*** Because Kryo is not thread safe. So, use ThreadLocal to store Kryo objects*/private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {Kryo kryo = new Kryo();kryo.register(RpcResponse.class);kryo.register(RpcRequest.class);return kryo;});@Overridepublic byte[] serialize(Object obj) {try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();Output output = new Output(byteArrayOutputStream)) {Kryo kryo = kryoThreadLocal.get();// Object->byte:将对象序列化为byte数组kryo.writeObject(output, obj);kryoThreadLocal.remove();return output.toBytes();} catch (Exception e) {throw new SerializeException("Serialization failed");}}@Overridepublic <T> T deserialize(byte[] bytes, Class<T> clazz) {try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);Input input = new Input(byteArrayInputStream)) {Kryo kryo = kryoThreadLocal.get();// byte->Object:从byte数组中反序列化出对对象Object o = kryo.readObject(input, clazz);kryoThreadLocal.remove();return clazz.cast(o);} catch (Exception e) {throw new SerializeException("Deserialization failed");}}}

Github 地址:https://github.com/EsotericSoftware/kryoopen in new window 。

Protobuf

Protobuf 出自于 Google,性能还比较优秀,也支持多种语言,同时还是跨平台的。就是在使用中过于繁琐,因为你需要自己定义 IDL 文件和生成对应的序列化代码。这样虽然不然灵活,但是,另一方面导致 protobuf 没有序列化漏洞的风险。

Protobuf 包含序列化格式的定义、各种语言的库以及一个 IDL 编译器。正常情况下你需要定义 proto 文件,然后使用 IDL 编译器编译成你需要的语言

一个简单的 proto 文件如下:

// protobuf的版本
syntax = "proto3";
// SearchRequest会被编译成不同的编程语言的相应对象,比如Java中的class、Go中的struct
message Person {//string类型字段string name = 1;// int 类型字段int32 age = 2;
}

Github 地址:https://github.com/protocolbuffers/protobufopen in new window。

ProtoStuff

由于 Protobuf 的易用性,它的哥哥 Protostuff 诞生了。

protostuff 基于 Google protobuf,但是提供了更多的功能和更简易的用法。虽然更加易用,但是不代表 ProtoStuff 性能更差。

Github 地址:https://github.com/protostuff/protostuffopen in new window。

hessian

hessian 是一个轻量级的,自定义描述的二进制 RPC 协议。hessian 是一个比较老的序列化实现了,并且同样也是跨语言的。

0ad3444b63e1943e3a1b1f6f103d5c82.png

dubbo RPC 默认启用的序列化方式是 hessian2 ,但是,Dubbo 对 hessian2 进行了修改,不过大体结构还是差不多。

总结

Kryo 是专门针对 Java 语言序列化方式并且性能非常好,如果你的应用是专门针对 Java 语言的话可以考虑使用,并且 Dubbo 官网的一篇文章中提到说推荐使用 Kryo 作为生产环境的序列化方式。(文章地址:https://dubbo.apache.org/zh/docs/v2.7/user/references/protocol/rest/open in new window)

cf55e9e5bdbcf12cead4635e452c37c6.png

像 Protobuf、 ProtoStuff、hessian 这类都是跨语言的序列化方式,如果有跨语言需求的话可以考虑使用。

除了我上面介绍到的序列化方式的话,还有像 Thrift,Avro 这些。

其他推荐阅读

  • 美团技术团队-序列化和反序列化:https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.htmlopen in new window
  • 在 Dubbo 中使用高效的 Java 序列化(Kryo 和 FST): https://dubbo.apache.org/zh-cn/docs/user/serialization.html

转自:Java 序列化详解 | JavaGuide


http://chatgpt.dhexx.cn/article/nqDt4Sqk.shtml

相关文章

JAVA的序列化

絮叨 Java序列化其实这个用的多不多&#xff0c;我觉得看公司的技术栈吧&#xff0c;如果用的是cloud那套&#xff0c;估计接触的会少点&#xff0c;但是也不是说没有&#xff0c;如果是dubbo那套的话&#xff0c;就多点&#xff0c;上次我们说Netty的编码解码的时候说到了谷歌…

MySQL卸载重新安装会遇到的问题及解决方式

好家伙&#xff0c;全踩雷了&#xff1a; 一.使用cd命令无法进入到其他盘符&#xff1f; 解决&#xff1a;在cd后加/d&#xff0c;两者之间用空格隔开 二.初始化时没有给出密码&#xff1f; 解决&#xff1a;仔细检查my.ini配置文件中的配置命令&#xff0c;一般是配置命令写错…

如何将MySQL卸载干净?

前言 前几天不知道什么情况电脑被莫名下载了MySQL8.0版本导致与本机的MySQL5.0版本冲突&#xff0c;导致MySQL无法正常运行&#xff0c;后来因为没有卸载干净MySQL导致一直下载不成功&#xff0c;导致我晚上失眠&#xff0c;现在让我们进入正题&#xff0c;看看如何将他卸载干…

【通关MySQL】Win11如何将MySQL卸载干净?

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f680;&#x1f680;系列专栏:【通关MySQL】 ✈️✈️本篇内容: MySQL如何卸载干净&#xff1f; ⛵⛵作者简介&#xff1a;一名双非本科大三在读的科班Java编程小白&#xff0c;道阻且长&#xff…

如何能将mysql卸载干净

本篇文章介绍一下如何干净卸载mysql。有一定的参考价值&#xff0c;有需要的朋友可以参考一下&#xff0c;希望对大家有所帮助。 第七步出现找不到相应的文件夹。第七步&#xff1a;路径是C:\Users\你的用户名字\AppData\Roaming\MySQL 有可能是隐藏的&#xff0c;需要显示隐藏…

如何将mysql卸载干净

一、在控制面板中卸载mysql软件 二、卸载过后删除C:\Program Files (x86)\MySQL该目录下剩余了所有文件&#xff0c;把mysql文件夹也删了 三、windowsR运行“regedit”文件&#xff0c;打开注册表 四、删除注册表&#xff1a;HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Service…

MySQL卸载安装

目录 MySQL卸载安装 1. MySQL卸载&#xff08;8.0版本&#xff09; 1.1停止MySQL服务 1.2 卸载MySQL相关组件 1.3删除MySQL安装目录 1.4删除MySQL数据目录 1.5 删除MySQL注册表信息&#xff08;重要&#xff09; 2. MySQL安装&#xff08;5.7.24版本&#xff09; 2.1安装…

mysql卸载不_mysql卸载不干净解决方法

一、在控制面板中卸载mysql软件&#xff0c;此时mysql没有卸载干净。 二、卸载过后删除C:Program Files (x86)MySQL该目录下剩余了所有文件&#xff0c;把mysql文件夹也删了 三、windowsR运行“regedit”文件&#xff0c;打开注册表 四、删除注册表&#xff1a;HKEY_LOCAL_MACH…

MySQL的卸载与安装(附管理工具Navicat SQLyog)

本文MySQL为5.1.51版本&#xff0c;附全文所需文件压缩包&#xff08;百度云 提取码&#xff1a;8778&#xff09;&#xff0c;其中SQLyog和Navicat为MySQL的图形化工具&#xff0c;任选其一安装即可&#xff08;建议Navicat&#xff09; 1.MySQL卸载 如果之前没有安装过MySQ…

【 Windows、Mac 】Mysql 卸载、安装

文章目录 Windows Mysql 卸载、安装一、彻底卸载mysql二、安装mysql 5.7三、安装 mysql 8四、MySql服务开机自启动失败 Mac 安装 mysql一、下载对应mysql版本二、安装三、重置密码 Windows Mysql 卸载、安装 一、彻底卸载mysql 1、关闭 MySql 服务。 2、在控制面板卸载mysq…

MySQL卸载、安装与使用

一、MySQL卸载 1.关闭服务 点击win&#xff0c;输入服务&#xff0c;找到MySQL的服务&#xff0c;右击停止该服务 2.删除服务 点击win&#xff0c;输入cmd&#xff0c;选择管理员身份运行。打开cmd&#xff0c;输入sc delete mysql 3删除配置的MySQL环境变量 MySQLHOME和…

MySQL卸载不干净问题

解决MySQL卸载不干净或安装的时候安装不成功&#xff0c;输入密码一直检查不通过问题 MySQL卸载不干净&#xff0c;之后安装就会遇到很多问题&#xff0c;我也遇到了这种情况&#xff0c;裝了好久&#xff0c;一到这里就会有问题&#xff0c;尝试卸载了很多次然后在从新裝&…

mysql卸载详细教程

完整卸载MySQL数据库 1、关掉mysql服务 直接搜索服务或者右键“我的电脑”&#xff0c;选择“管理”&#xff0c;打开计算机管理&#xff0c;选择“服务”右键MySQL服务&#xff0c;选择“停止” 2、 卸载mysql程序 开始菜单->控制面板->程序和功能 3、 删除计算机上…

免装版mysql卸载

免装版mysql卸载: 1.停止mysql服务:以管理员身份打开cmd执行:net stop mysql 2.删除Mysql的注册表 winr 输入regedit打开注册表: 3.管理员cmd执行:mysqld -remove mysql 4.删除mysql之前的安装包:

MySQL卸载与安装

目录 1.MySQL卸载2.MySQL安装2.1获取mysql.zip2.1.1百度网盘链接2.1.2官网获取 2.2解压到自己想要安装的目录2.3初始化 MySQL2.4安装 MySQL 服务2.5修改登录密码2.6配置环境变量2.7其他问题2.7.1重置 MySQL 系统2.7.2忘记密码2.7.3版本切换2.7.4编码和 my.ini 设置2.7.5修改密码…

MySQL卸载

文章目录 将现有MySQL服务关闭&#xff08;必做&#xff09;确定自己MySQL的安装位置&#xff08;辅助工作&#xff09;方法1&#xff1a;适用于之前成功安装MySQL并完成环境变量配置的情况方法2&#xff1a;暴力搜索-适用于之前修改过安装位置的情况 在系统中卸载MySQL&#x…

mysql的卸载与安装(超详细)

1、卸载 1、首先&#xff0c;停止window的MySQL服务&#xff0c;【windows键R 】打开运行框&#xff0c;输入【services.msc】打开&#xff08;或者找到“控制面板”-> “管理工具”-> “服务”&#xff0c;停止MySQL后台服务&#xff09;服务管理器&#xff0c;停止MyS…

Windows10 彻底卸载 MySQL

如果要重新安装MySQL就必须将之前的MySQL进行彻底的卸载&#xff0c;本文将详细地介绍如何彻底卸载Windows10里的MySQL。 第一步&#xff1a;关闭MySQL服务器&#xff0c;MySQL服务器的关闭有两种方式。 方法一&#xff1a;在管理员终端命令里输入&#xff1a; net stop mys…

Java List转String数组与String数组转List

1. String数组转List String title "\t 10月上旬\t 10月中旬\t 10月下旬"; String[] arrTitles title.split("\t"); List<String> titleList Arrays.asList(arrTitles);1.1 方法一&#xff08;不推荐&#xff09; List<String> titleLis…

list转Array

集合转数组 必须使用集合的 toArray(T[] array)&#xff0c;传入的是类型完全 一样的数组&#xff0c;大小就是 list.size()。 说明&#xff1a; 使用 toArray 带参方法&#xff0c;入参分配的数组空间不够大时&#xff0c;toArray 方法内部将重新分配内存空间&#xff0c;并…