详解序列化

news2025/1/11 2:16:15

目录

1.什么是序列化

2.JAVA中的序列化

2.1.成员变量必须可序列化

2.2.transient关键字,可避免被序列化

2.3.无法更新状态

2.4.serialVersionUID

3.JDK序列化算法

4.序列化在实际中的一些应用


1.什么是序列化

序列化就是将对象转换为二进制格式的过程。对象转为二进制后,可以方便进行存储或者在网络中传输。要将对象转为二进制,首先就要用一种数据结构来描述对象,这样进行对象的网络传输的时候,发送方和接收方才能按照约定好的格式来序列化、反序列化对象。不同的序列化算法就是用不同的数据结构来描述对象,序列化算法很多,这里举几个常见的:

  • json

  • xml

  • yaml

  • Java Serialization

前三种见名知意,就是分别用json、xml、yaml来描述对象,第四种Java Serialization是JDK默认的序列化算法,其使用了一种称为 Java Object Serialization Stream Protocol 的二进制格式来描述JAVA对象。

2.JAVA中的序列化

JDK种提供了Serializable接口用来声明哪些类可以被序列化,提供了ObjectOutputStream、ObjectOutputStream来进行序列化和反序列化。

JAVA的序列化中有几个注意点:

  • 成员变量必须可序列化

  • transient关键字,可避免被序列化

  • 无法更新状态

  • serialVersionUID

2.1.成员变量必须可序列化

如果所要序列化的对象的成员属性中含有对其他对象的引用,要求所引用的对象也必须是可序列化的(实现serializable接口),否则会序列化失败。

订单对象中包含一个产品对象,Order实现了序列化接口,但是product没有实现序列化接口:

序列化Order的时候会报错:

2.2.transient关键字,可避免被序列化

用transient修饰属性:

可以看到属性值不会被序列化出去,其会是默认值:

2.3.无法更新状态

由于java序利化算法不会重复序列化同一个对象,如果对象的内容更改后,,再次序列化,并不会再次将此对象转换为字节序列。

我们对同一个对象序列化两次,然后输出其属性值:

可以看到其实只有第一次序列化是生效的:

2.4.serialVersionUID

序列化版本号,类似于乐观锁中的版本号,用来保证序列化后的字节序列没有被改动过,反序列化回来后和原来的程序是兼容的。

serialVersionUID不会自动改变,而是留给程序员手动更改的一个版本号标志位。更改了序列化文件的程序员一并更改版本号提示后来的人文件被更改过。

如果在反序列化时,类的 serialVersionUID 与序列化时的版本号不匹配,那么会抛出 InvalidClassException 异常,表示类的版本不兼容,无法进行反序列化。

3.JDK序列化算法

Java Object Serialization Stream Protocol规定整个对象序列化后的文件由三部分组成:

  1. 头部(Header):包含魔数(Magic Number)和版本号(Version Number)。魔数标识了该流是 Java 序列化流,版本号用于指定序列化协议的版本。

  2. 类描述符表(Class Descriptor Table):包含了序列化流中所引用的类的描述符信息。每个类描述符包括类的名称、序列化编号、序列化版本号等信息。

  3. 对象数据(Object Data):按照序列化顺序包含了被序列化对象的状态信息。这包括了对象的实例变量、类信息等。

以上一节我们在D盘下生成了一个名叫Order.txt的序列化文件为例,我们来读一读JAVA的序列化文件。

要注意的是如果直接打开,因为编码的原因看见的会是乱码,需要用16进制的方式,打开它来看看,要注意的是普通的文本工具都没办法用16进制的方式直接打开文件,这里我们用代码来将文本中的内容以16进制的方式输出,代码如下:

public static void main(String[] args) throws IOException {
        displayFileInHex("D:\\Order.txt");
    }

    private static void displayFileInHex(String filePath) throws IOException {
        try (FileInputStream fileIn = new FileInputStream(filePath)) {
            int bytesRead;
            byte[] buffer = new byte[16];

            while ((bytesRead = fileIn.read(buffer)) != -1) {
                // 打印十六进制内容
                for (int i = 0; i < bytesRead; i++) {
                    System.out.printf("%02X ", buffer[i]);
                }

                // 填充缺失的位置
                if (bytesRead < 16) {
                    int missingBytes = 16 - bytesRead;
                    for (int i = 0; i < missingBytes; i++) {
                        System.out.print("   ");
                    }
                }
                System.out.println("\n");
            }
        }
    }

输出结果:

用Java Object Serialization Stream Protocol来解析一下上面的字节内容:

  1. 头部(Header):AC ED 表示 Java 序列化文件的标识符。

  2. 版本号:00 05 表示版本号为 5。

  3. 对象数据:73 72 00 0E 63 6F 6D 2E 65 72 79 69 2E 4F 72 64 65 72 是一个类描述符,指明被序列化对象所属的类为 com.eryi.Order

  4. 对象数据:9D F0 BD D3 7C 8B DA 85 02 00 02 4C 00 0B 6F 72 64 65 72 4E 75 6D 62 65 72 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 是一个对象的实例数据,包含了对象的状态信息。

  5. 对象数据:4C 00 07 70 72 6F 64 75 63 74 是一个对象的实例变量的描述符,指明变量名为 product

  6. 对象数据:74 00 12 4C 63 6F 6D 2F 65 72 79 69 2F 50 72 6F 64 75 63 74 3B 是一个字符串,表示变量值为 "com.eryi.Product"

  7. 对象数据:78 70 74 00 03 34 35 36 是一个对象的实例变量的描述符,指明变量名为 xpt

  8. 对象数据:73 72 00 10 63 6F 6D 2E 65 72 79 69 2E 50 72 6F 64 75 63 74 是一个类描述符,指明变量类型为 com.eryi.Product

  9. 对象数据:15 0D 2C B6 A0 EE 95 4F 02 00 02 是一个对象的实例数据,包含了变量的状态信息。

  10. 对象数据:4C 00 04 6E 61 6D 65 是一个对象的实例变量的描述符,指明变量名为 name

  11. 对象数据:71 00 7E 00 01 是一个字符串,表示变量值为 "name"

  12. 对象数据:4C 00 05 70 72 69 63 65 是一个对象的实例变量的描述符,指明变量名为 price

  13. 对象数据:71 00 7E 00 01 是一个字符串,表示变量值为 "price"

  14. 对象数据:78 70 74 00 0C E6 B2 83 E5 B0 94 E6 B2 83 53 36 30 是一个对象的实例变量的描述符,指明变量名为 xpt

  15. 对象数据:74 00 03 33 30 57 是一个字符串,表示变量值为 "30W"

4.序列化在实际中的一些应用

首先我们要知道序列化是可以跨JVM的,JDK的序列化算法只是规定了数据结构,所以可以在一个JVM上序列化,然后在另一个JVM中进行反序列化,这也就是说序列化可以用来进行通信时的数据传输。并且序列化在进行数据传输上具有很好的性能优势,因为序列化是直接将对象转为了二进制,接收端收到数据后直接反序列化就可以得到对象。如果是以JSON之类的文本结构传输数据,那么接收端收到数据后要首先将二进制数据流转为文本结构,然后再解析文本结构将其转为对象。整个过程比起序列化和反序列化来,多了很多中间步骤,性能上肯定要慢很多。

由于序列化具有上面这样的优势,其被dubbo所采用。dubbo作为以高性能著称的RPC框架,其高性能有一方面就体现在使用了序列化上。dubbo自定义了报文,然后通过序列化的方式将数据直接塞进自定义的报文结构中,接收端收到后直接反序列化就可以得到数据。

所以序列化在追求一些高性能的通信场景下会是作为参数转换的一个不错选择。

同时,序列化又存在安全隐患,由于serialVersionUID和数据没有任何关系,修改属性的数据值后,仍然可以反序列化回来,而且任何JVM拿到序列化的数据都可以进行反序列化,会存在数据被拦截然后恶意修改的风险。不过这个问题并不是序列化所独有的问题,只要是没有加密机制的通信协议都会存在这个问题,相比于同样是透明传输的HTTP来说,用序列化在JAVA EE体系中传数据对象确实性能更优。

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

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

相关文章

MySQL的MMM高可用

MySQL的MMM高可用 一、MMM简介二、搭建MySQL的MMM高可用1、准备环境2、 搭建 MySQL 多主多从模式3、安装配置 MySQL-MMM4、故障测试5、客户端测试 一、MMM简介 MMM&#xff08;Master-Master replication manager for MvSQL&#xff0c;MySQL主主复制管理器&#xff09;是一套…

访问学者申请英语口语怎样顺利通关

想要成功申请成为访问学者&#xff0c;英语口语的流利表达是非常重要的。下面知识人网小编整理的一些帮助你顺利通关的建议&#xff1a; 1. 提前准备&#xff1a;在面试之前&#xff0c;充分准备各种常见问题的回答。练习口语表达&#xff0c;加强词汇和语法的掌握。可以通过与…

如何学习PHP语法和基本概念? - 易智编译EaseEditing

学习PHP语法和基本概念是成为一个合格的PHP开发者的第一步。以下是一些学习PHP语法和基本概念的建议&#xff1a; 官方文档&#xff1a; PHP官方提供了详细的文档&#xff0c;包括语言参考、函数参考和示例等。阅读官方文档是学习PHP语法和基本概念的最佳途径。你可以访问PHP…

企业如何数字化转型?企业数字化转型面临哪些挑战?

一文看懂&#xff1a;传统企业数字化转型会面临哪些困难与挑战&#xff1f;如何有效解决&#xff1f; 目前&#xff0c;数字技术的发展已经从互联网、大数据&#xff0c;迈入了AI人工智能时代。而企业也如逆水行舟、不进则退&#xff0c;如果不进行数字化转型&#xff0c;企业…

AI芯片的基础

前置基础 AI芯片其实就是AI算法的专用处理器&#xff0c;像CPU的话是一个通用处理器&#xff0c;CPU按照逻辑可以分为三个模块&#xff1a;控制模块&#xff0c;运算模块&#xff0c;存储模块&#xff1b;其中控制单元有指令寄存器和指令译码器&#xff0c;根据用户预先编译好…

关于Apache Dubbo反序列化漏洞(CVE-2023-23638)的预警提示与对应的Zookeeper版本

公司在升级dubbo过程中因zookeeper版本不匹配&#xff0c;导致服务注册和调用出现异常 一、漏洞详情 Apache Dubbo是一款高性能、轻量级的开源Java服务框架。 Apache官方发布安全公告&#xff0c;修复了Apache Dubbo中的一个反序列化漏洞&#xff08;CVE-2023-23638&#xff…

系统架构设计师 5:软件工程

一、软件工程 1 软件过程模型 软件要经历从需求分析、软件设计、软件开发、运行维护&#xff0c;直至被淘汰这样的全过程&#xff0c;这个全过程称为软件的生命周期。 为了使软件生命周期中的各项任务能够有序地按照规程进行&#xff0c;需要一定的工作模型对各项任务给予规…

不会设计也能轻松制作商品宣传海报图片,只要跟着这个教程走

促销活动是商家吸引顾客的重要方式之一&#xff0c;而宣传海报则是促销活动中的主要宣传工具之一。好的宣传海报可以让顾客对活动产生兴趣&#xff0c;提高产品的曝光率。然而&#xff0c;对于小型商家来说&#xff0c;往往没有专业的设计人员&#xff0c;如何制作出符合自己需…

项目开发中的安全问题怎么处理?

目录 1.客户的数据不可信 2. 客户端提交的参数需要校验 3.请求头里的内容出现错误 1.客户的数据不可信 PostMapping("/order") public void wrong(RequestBody Order order) {this.createOrder(order); } 对应的实体类如下&#xff1a; Data public class Order …

[框架]MyBatis框架

关于MyBatis框架 MyBatis框架的主要作用&#xff1a;实现并简化数据库编程。 MyBatis框架的依赖项 MyBatis框架的依赖项是&#xff1a;mybatis&#xff0c;但&#xff0c;通常还应该再添加&#xff1a;mybatis-spring、spring-jdbc、mysql或其它数据库的依赖项、数据库连接池…

FreeRTOS_列表和列表项

目录 1. 什么是列表和列表项&#xff1f; 1.1 列表 1.2 列表项 1.3 迷你列表项 2. 列表和列表项初始化 2.1 列表初始化 2.2 列表项初始化 3. 列表项插入 3.1 列表项插入函数分析 3.2 列表项插入过程图示 3.2.1 插入值为 40 的列表项 3.2.2 插入值为 60 的列表项 3…

c++ while(i--) 的执行顺序

问&#xff1a;while (i--) {语句B} 的执行顺序是什么&#xff1f; 首先执行i--,再执行 while ,再执行语言B. 一个案例&#xff1a; 案例中count 的初始值为-3. 第一个打出的为-2&#xff0c; 证明的之前的判断流程。 附录&#xff1a; while(条件){//语句A} 的执行逻辑&…

聊聊PCIe协议的BDF

[摘要]&#xff1a;本文主要介绍 PCIe 协议中 Bus、Device 和 Function 的基本定义。 PCIe 总线的拓扑结构就像一颗葡萄树&#xff1a;树根相当于 PCIe Root Complex&#xff0c;树干和树枝相当于 PCIe Bus&#xff0c;一整串葡萄相当于 PCIe Device&#xff0c;一颗葡萄相当于…

c++构造基类

c构造基类 c构造基类采用初始化列表 基类的成员变量由基类的构造函数初始化 派生类的成员变量由派生类的构造函数初始化 基类&#xff1a; 派生类: 调用

微信扫码对接

微信扫码对接&#xff0c;如果刚开始没有资源进行对接。 可以选择先申请微信公众测试平台进行测试&#xff0c;地址如下 微信公众平台 申请步骤等等简单的就不说了&#xff0c;本文主要说一下需要注意的坑。 1.首先需要一个外网地址&#xff0c;做本地映射&#xff0c;否则…

掌握这些容易被忽略的Vue组件细节,提升开发效率,事半功倍!

组件允许我们将 UI 划分为独立的、可重用的部分&#xff0c;并且可以对每个部分进行单独的思考。 组件在日常开发的重要性不言而喻&#xff0c;掌握下述细则&#xff0c;可以让你在开发中事半功倍&#xff01; Props defineProps() 宏中的参数不可以访问 <script setup&…

mysql的高可用架构之mmm

目录 一、mmm的相关知识 1&#xff09;mmm架构的概念 2&#xff09;MMM 高可用架构的重要组件 3&#xff09;mmm故障切换流程 二、mmm高可用双主双从架构部署 实验设计 实验需求 实验组件部署 具体实验步骤 步骤一&#xff1a; 搭建 MySQL 多主多从模式 &#…

Uniapp中简单弹出层的使用

图示 思路 当派工单这个输入框获取到焦点后&#xff0c;弹出弹出层选择数据。 1、定义这个输入框 <view class"cu-form-group"><view class"title"><text class"text-red">*</text>派工单号: </view><input…

Mysql查询语句优化方法

查询优化 小表驱动大表 优化原则&#xff1a;对于MySQL数据库而言&#xff0c;永远都是小表驱动大表。 /** * 举个例子&#xff1a;可以使用嵌套的for循环来理解小表驱动大表。 * 以下两个循环结果都是一样的&#xff0c;但是对于MySQL来说不一样&#xff0c; * 第一种可以理解…

第1章-Java语言概述

Java基础知识图解 1. Java语言概述 1.1 Java简史 是SUN(Stanford University Network&#xff0c;斯坦福大学网络公司 ) 1995年推出的一门高级编程语言。是一种面向Internet的编程语言。 Java一开始富有吸引力是因为Java程序可以在Web浏览器中运行。 这些Java程序被称为Java小…