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

本文涉及的产品
.cn 域名,1个 12个月
简介: 本节书摘来华章计算机《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

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

  • 对象流输出中包含所有对象的类型和数据域。
  • 每个对象都被赋予一个序列号。
  • 相同对象的重复出现将被存储为对这个对象的序列号的引用。
相关文章
|
1月前
|
存储 安全 Java
Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
【10月更文挑战第17天】Java Map新玩法:探索HashMap和TreeMap的高级特性,让你的代码更强大!
59 2
|
12天前
|
JSON JavaScript Java
对比JSON和Hessian2的序列化格式
通过以上对比分析,希望能够帮助开发者在不同场景下选择最适合的序列化格式,提高系统的整体性能和可维护性。
14 3
|
17天前
|
JSON 数据格式 索引
Python中序列化/反序列化JSON格式的数据
【11月更文挑战第4天】本文介绍了 Python 中使用 `json` 模块进行序列化和反序列化的操作。序列化是指将 Python 对象(如字典、列表)转换为 JSON 字符串,主要使用 `json.dumps` 方法。示例包括基本的字典和列表序列化,以及自定义类的序列化。反序列化则是将 JSON 字符串转换回 Python 对象,使用 `json.loads` 方法。文中还提供了具体的代码示例,展示了如何处理不同类型的 Python 对象。
|
23天前
|
存储 安全 Java
🌟Java零基础-反序列化:从入门到精通
【10月更文挑战第21天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
63 5
|
25天前
|
存储 缓存 安全
🌟Java零基础:深入解析Java序列化机制
【10月更文挑战第20天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
24 3
|
27天前
|
存储 安全 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第22天】在Java的世界里,对象序列化和反序列化是数据持久化和网络传输的关键技术。本文将带你了解如何在Java中实现对象的序列化与反序列化,并探讨其背后的原理。通过实际代码示例,我们将一步步展示如何将复杂数据结构转换为字节流,以及如何将这些字节流还原为Java对象。文章还将讨论在使用序列化时应注意的安全性问题,以确保你的应用程序既高效又安全。
|
30天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
58 3
|
30天前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
24 2
|
30天前
|
JSON 前端开发 数据格式
前端的全栈之路Meteor篇(五):自定义对象序列化的EJSON介绍 - 跨设备的对象传输
EJSON是Meteor框架中扩展了标准JSON的库,支持更多数据类型如`Date`、`Binary`等。它提供了序列化和反序列化功能,使客户端和服务器之间的复杂数据传输更加便捷高效。EJSON还支持自定义对象的定义和传输,通过`EJSON.addType`注册自定义类型,确保数据在两端无缝传递。
|
1月前
|
存储 Java
Java编程中的对象序列化与反序列化
【10月更文挑战第9天】在Java的世界里,对象序列化是连接数据持久化与网络通信的桥梁。本文将深入探讨Java对象序列化的机制、实践方法及反序列化过程,通过代码示例揭示其背后的原理。从基础概念到高级应用,我们将一步步揭开序列化技术的神秘面纱,让读者能够掌握这一强大工具,以应对数据存储和传输的挑战。
下一篇
无影云桌面