《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一2.4.2 理解对象序列化的文件格式

简介: 本节书摘来华章计算机《Java核心技术 卷Ⅱ 高级特性(原书第10版)》一书中的第2章 ,第2.4.2节,[美] 凯S.霍斯特曼(Cay S. Horstmann) 著陈昊鹏 译 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

2.4.2 理解对象序列化的文件格式

对象序列化是以特殊的文件格式存储对象数据的,当然,你不必了解文件中表示对象的确切字节序列,就可以使用writeObject/readObject方法。但是,我们发现研究这种数据格式对于洞察对象流化的处理过程非常有益。因为其细节显得有些专业,所以如果你对其实现不感兴趣,则可以跳过这一节。
每个文件都是以下面这两个字节的“魔幻数字”开始的
image

后面紧跟着对象序列化格式的版本号,目前是
image

(我们在本节中统一使用十六进制数字来表示字节。)然后,是它包含的对象序列,其顺序即它们存储的顺序。
字符串对象被存为
image

字符串中的Unicode字符被存储为修订过的UTF-8格式。
当存储一个对象时,这个对象所属的类也必须存储。这个类的描述包含

  • 类名。
  • 序列化的版本唯一的ID,它是数据域类型和方法签名的指纹。
  • 描述序列化方法的标志集。
  • 对数据域的描述。

指纹是通过对类、超类、接口、域类型和方法签名按照规范方式排序,然后将安全散列算法(SHA)应用于这些数据而获得的。
SHA是一种可以为较大的信息块提供指纹的快速算法,不论最初的数据块尺寸有多大,这种指纹总是20个字节的数据包。它是通过在数据上执行一个灵巧的位操作序列而创建的,这个序列在本质上可以百分之百地保证无论这些数据以何种方式发生变化,其指纹也都会跟着变化。(关于SHA的更多细节,可以查看一些参考资料,例如William Stallings所著的《Cryptography and Network Security: Principles and Practice》第7版[Prentice Hall, 2016]。)但是,序列化机制只使用了SHA码的前8个字节作为类的指纹。即便这样,当类的数据域或方法发生变化时,其指纹跟着变化的可能性还是非常大。
在读入一个对象时,会拿其指纹与它所属的类的当前指纹进行比对,如果它们不匹配,那么就说明这个类的定义在该对象被写出之后发生过变化,因此会产生一个异常。在实际情况下,类当然是会演化的,因此对于程序来说,读入较旧版本的对象可能是必需的。我们将在2.4.5节中讨论这个问题。
下面表示了类标识符是如何存储的:

  • 72
  • 2字节的类名长度
  • 类名
  • 8字节长的指纹
  • 1字节长的标志
  • 2字节长的数据域描述符的数量
  • 数据域描述符
  • 78(结束标记)
  • 超类类型(如果没有就是70)

标志字节是由在java.io.ObjectStreamConstants中定义的3位掩码构成的:
image

我们会在本章稍后讨论Externalizable接口。可外部化的类提供了定制的接管其实例域输出的读写方法。我们要写出的这些类实现了Serializable接口,并且其标志值为02,而可序列化的java.util.Date类定义了它自己的readObject/writeObject方法,并且其标志值为03。
每个数据域描述符的格式如下:

  • 1字节长的类型编码
  • 2字节长的域名长度
  • 域名
  • 类名(如果域是对象)

其中类型编码是下列取值之一:
image

当类型编码为L时,域名后面紧跟域的类型。类名和域名字符串不是以字符串编码74开头的,但域类型是。域类型使用的是与域名稍有不同的编码机制,即本地方法使用的
格式。
例如,Employee类的薪水域被编码为:
image

下面是Employee类完整的类描述符:
image
image

这些描述符相当长,如果在文件中再次需要相同的类描述符,可以使用一种缩写版:
image

这个序列号将引用到前面已经描述过的类描述符,我们稍后将讨论编号模式。
对象将被存储为:
image

例如,下面展示的就是Employee对象如何存储:
image

正如你所看见的,数据文件包含了足够的信息来恢复这个Employee对象。
数组总是被存储成下面的格式:
image

在类描述符中的数组类名的格式与本地方法中使用的格式相同(它与在其他的类描述符中的类名稍微有些差异)。在这种格式中,类名以L开头,以分号结束。
例如,3个Employee对象构成的数组写出时就像下面一样:
image

注意,Employee对象数组的指纹与Employee类自身的指纹并不相同。
所有对象(包含数组和字符串)和所有的类描述符在存储到输出文件时都被赋予了一个序列号,这个数字以00 7E 00 00开头。
我们已经看到过,任何给定的类其完整的类描述符只保存一次,后续的描述符将引用它。例如,在前面的示例中,对Date类的重复引用就被编码为:
image

相同的机制还被用于对象。如果要写出一个对之前存储过的对象的引用,那么这个引用也会以完全相同的方式存储,即71后面跟随序列号,从上下文中可以很清楚地了解这个特殊的序列引用表示的是类描述符还是对象。
最后,空引用被存储为:
image

下面是前面小节中ObjectRefTest程序的带注释的输出。如果你喜欢,可以运行这个程序,然后查看其数据文件employee.dat的十六进制码,并将其与注释列表比较。在输出中接近结束部分的几行重要编码展示了对之前存储过的对象的引用。
image
image

当然,研究这些编码大概与阅读常用的电话号码簿一样枯燥。了解确切的文件格式确实不那么重要(除非你试图通过修改数据来达到不可告人的目的),但是对象流对其所包含的所有对象都有详细描述,并且这些充足的细节可以用来重构对象和对象数组,因此了解它还是大有益处的。
你应该记住:

  • 对象流输出中包含所有对象的类型和数据域。
  • 每个对象都被赋予一个序列号。
  • 相同对象的重复出现将被存储为对这个对象的序列号的引用。
目录
打赏
0
0
0
0
1408
分享
相关文章
重学Java基础篇—Java对象创建的7种核心方式详解
本文全面解析了Java中对象的创建方式,涵盖基础到高级技术。包括`new关键字`直接实例化、反射机制动态创建、克隆与反序列化复用对象,以及工厂方法和建造者模式等设计模式的应用。同时探讨了Spring IOC容器等框架级创建方式,并对比各类方法的适用场景与优缺点。此外,还深入分析了动态代理、Unsafe类等扩展知识及注意事项。最后总结最佳实践,建议根据业务需求选择合适方式,在灵活性与性能间取得平衡。
50 3
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
Java对象创建和访问
Java对象创建过程包括类加载检查、内存分配(指针碰撞或空闲列表)、内存初始化、对象头设置及初始化方法执行。访问方式有句柄和直接指针两种,前者稳定但需额外定位,后者速度快。对象创建涉及并发安全、垃圾回收等机制。
Java对象创建和访问
深潜数据海洋:Java文件读写全面解析与实战指南
通过本文的详细解析与实战示例,您可以系统地掌握Java中各种文件读写操作,从基本的读写到高效的NIO操作,再到文件复制、移动和删除。希望这些内容能够帮助您在实际项目中处理文件数据,提高开发效率和代码质量。
37 4
解锁“分享文件”高效密码:探秘 Java 二叉搜索树算法
在信息爆炸的时代,文件分享至关重要。二叉搜索树(BST)以其高效的查找性能,为文件分享优化提供了新路径。本文聚焦Java环境下BST的应用,介绍其基础结构、实现示例及进阶优化。BST通过有序节点快速定位文件,结合自平衡树、多线程和权限管理,大幅提升文件分享效率与安全性。代码示例展示了文件插入与查找的基本操作,适用于大规模并发场景,确保分享过程流畅高效。掌握BST算法,助力文件分享创新发展。
Java中判断一个对象是否是空内容
在 Java 中,不同类型的对象其“空内容”的定义和判断方式各异。对于基本数据类型的包装类,空指对象引用为 null;字符串的空包括 null、长度为 0 或仅含空白字符,可通过 length() 和 trim() 判断;集合类通过 isEmpty() 方法检查是否无元素;数组的空则指引用为 null 或长度为 0。
Java快速入门之类、对象、方法
本文简要介绍了Java快速入门中的类、对象和方法。首先,解释了类和对象的概念,类是对象的抽象,对象是类的具体实例。接着,阐述了类的定义和组成,包括属性和行为,并展示了如何创建和使用对象。然后,讨论了成员变量与局部变量的区别,强调了封装的重要性,通过`private`关键字隐藏数据并提供`get/set`方法访问。最后,介绍了构造方法的定义和重载,以及标准类的制作规范,帮助初学者理解如何构建完整的Java类。
|
3月前
|
Object取值转java对象
通过本文的介绍,我们了解了几种将 `Object`类型转换为Java对象的方法,包括强制类型转换、使用 `instanceof`检查类型和泛型方法等。此外,还探讨了在集合、反射和序列化等常见场景中的应用。掌握这些方法和技巧,有助于编写更健壮和类型安全的Java代码。
111 17
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
460 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
3月前
|
java代码优化:判断内聚到实体对象中和构造上下文对象传递参数
通过两个常见的java后端实例场景探讨代码优化,代码不是优化出来的,而是设计出来的,我们永远不可能有专门的时间去做代码优化,优化和设计在平时
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等