当前位置: 首页 > 编程日记 > 正文

Java序列化的机制和原理

有关Java对象的序列化和反序列化也算是Java基础的一部分,下面对Java序列化的机制和原理进行一些介绍。

Java序列化算法透析

Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。Java序列化API提供一种处理对象序列化的标准机制。在这里你能学到如何序列化一个对象,什么时候需要序列化以及Java序列化的算法,我们用一个实例来示范序列化以后的字节是如何描述一个对象的信息的。

序列化的必要性

Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生。

如何序列化一个对象

一个对象能够序列化的前提是实现Serializable接口,Serializable接口没有方法,更像是个标记。有了这个标记的Class就能被序列化机制处理。

  1. import java.io.Serializable;  
  2.  
  3. class TestSerial implements Serializable {  
  4.  
  5.        public byte version = 100;  
  6.  
  7.        public byte count = 0;  
  8.  

然后我们写个程序将对象序列化并输出。ObjectOutputStream能把Object输出成Byte流。我们将Byte流暂时存储到temp.out文件里。

  1. public static void main(String args[]) throws IOException {  
  2.  
  3.        FileOutputStream fos = new FileOutputStream("temp.out");  
  4.  
  5.        ObjectOutputStream oos = new ObjectOutputStream(fos);  
  6.  
  7.        TestSerial ts = new TestSerial();  
  8.  
  9.        oos.writeObject(ts);  
  10.  
  11.        oos.flush();  
  12.  
  13.        oos.close();  
  14.  

如果要从持久的文件中读取Bytes重建对象,我们可以使用ObjectInputStream。

  1. public static void main(String args[]) throws IOException {  
  2.  
  3.        FileInputStream fis = new FileInputStream("temp.out");  
  4.  
  5.        ObjectInputStream oin = new ObjectInputStream(fis);  
  6.  
  7.        TestSerial ts = (TestSerial) oin.readObject();  
  8.  
  9.        System.out.println("version="+ts.version);  
  10.  

执行结果为

100.

对象的序列化格式

将一个对象序列化后是什么样子呢?打开刚才我们将对象序列化输出的temp.out文件,以16进制方式显示。内容应该如下:

AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 6573 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 0563 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 7870 00 64

这一坨字节就是用来描述序列化以后的

TestSerial对象的,我们注意到TestSerial类中只有两个域:

public byte version = 100;

public byte count = 0;

且都是byte型,理论上存储这两个域只需要2个byte,但是实际上temp.out占据空间为51bytes,也就是说除了数据以外,还包括了对序列化对象的其他描述。

Java的序列化算法

序列化算法一般会按步骤做如下事情:

◆将对象实例相关的类元数据输出。

◆递归地输出类的超类描述直到不再有超类。

◆类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。

◆从上至下递归输出实例的数据

我们用另一个更完整覆盖所有可能出现的情况的例子来说明:

  1. class parent implements Serializable {  
  2.  
  3.        int parentVersion = 10;  
  4.  
  5. }  
  6.  
  7.    
  8.  
  9. class contain implements Serializable{  
  10.  
  11.        int containVersion = 11;  
  12.  
  13. }  
  14.  
  15. public class SerialTest extends parent implements Serializable {  
  16.  
  17.        int version = 66;  
  18.  
  19.        contain con = new contain();  
  20.  
  21.    
  22.  
  23.        public int getVersion() {  
  24.  
  25.               return version;  
  26.  
  27.        }  
  28.  
  29.        public static void main(String args[]) throws IOException {  
  30.  
  31.               FileOutputStream fos = new FileOutputStream("temp.out");  
  32.  
  33.               ObjectOutputStream oos = new ObjectOutputStream(fos);  
  34.  
  35.               SerialTest st = new SerialTest();  
  36.  
  37.               oos.writeObject(st);  
  38.  
  39.               oos.flush();  
  40.  
  41.               oos.close();  
  42.  
  43.        }  
  44.  

这个例子是相当的直白啦。SerialTest类实现了Parent超类,内部还持有一个Container对象。

序列化后的格式如下:

AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65

73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07

76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09

4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72

65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00

0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70

00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74

61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00

0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78

70 00 00 00 0B

我们来仔细看看这些字节都代表了啥。开头部分,见颜色

  1. AC ED: STREAM_MAGIC. 声明使用了序列化协议.
  2. 00 05: STREAM_VERSION. 序列化协议版本.
  3. 0x73: TC_OBJECT. 声明这是一个新的对象.

序列化算法的第一步就是输出对象相关类的描述。例子所示对象为SerialTest类实例,因此接下来输出SerialTest类的描述。见颜色

  1. 0x72: TC_CLASSDESC. 声明这里开始一个新Class。
  2. 00 0A: Class名字的长度.
  3. 53 65 72 69 61 6c 54 65 73 74: SerialTest,Class类名.
  4. 05 52 81 5A AC 66 02 F6: SerialVersionUID, 序列化ID,如果没有指定,则会由算法随机生成一个8byte的ID.
  5. 0x02: 标记号. 该值声明该对象支持序列化。
  6. 00 02: 该类所包含的域个数。

接下来,算法输出其中的一个域,int version=66;见颜色

  1. 0x49: 域类型. 49 代表"I", 也就是Int.
  2. 00 07: 域名字的长度.
  3. 76 65 72 73 69 6F 6E: version,域名字描述.

然后,算法输出下一个域,contain con = new contain();这个有点特殊,是个对象。描述对象类型引用时需要使用JVM的标准对象签名表示法,见颜色

  1. 0x4C: 域的类型.
  2. 00 03: 域名字长度.
  3. 63 6F 6E: 域名字描述,con
  4. 0x74: TC_STRING. 代表一个new String.用String来引用对象。
  5. 00 09: 该String长度.
  6. 4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, JVM的标准对象签名表示法.
  7. 0x78: TC_ENDBLOCKDATA,对象数据块结束的标志

.接下来算法就会输出超类也就是Parent类描述了,见颜色

  1. 0x72: TC_CLASSDESC. 声明这个是个新类.
  2. 00 06: 类名长度.
  3. 70 61 72 65 6E 74: parent,类名描述。
  4. 0E DB D2 BD 85 EE 63 7A: SerialVersionUID, 序列化ID.
  5. 0x02: 标记号. 该值声明该对象支持序列化.
  6. 00 01: 类中域的个数.

下一步,输出parent类的域描述,int parentVersion=100;同见颜色

  1. 0x49: 域类型. 49 代表"I", 也就是Int.
  2. 00 0D: 域名字长度.
  3. 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion,域名字描述。
  4. 0x78: TC_ENDBLOCKDATA,对象块结束的标志。
  5. 0x70: TC_NULL, 说明没有其他超类的标志。.

到此为止,算法已经对所有的类的描述都做了输出。下一步就是把实例对象的实际值输出了。这时候是从parent Class的域开始的,见颜色

  1. 00 00 00 0A: 10, parentVersion域的值.

还有SerialTest类的域:

  1. 00 00 00 42: 66, version域的值.

再往后的bytes比较有意思,算法需要描述contain类的信息,要记住,现在还没有对contain类进行过描述,见颜色

  1. 0x73: TC_OBJECT, 声明这是一个新的对象.
  2. 0x72: TC_CLASSDESC声明这里开始一个新Class.
  3. 00 07: 类名的长度.
  4. 63 6F 6E 74 61 69 6E: contain,类名描述.
  5. FC BB E6 0E FB CB 60 C7: SerialVersionUID, 序列化ID.
  6. 0x02: Various flags. 标记号. 该值声明该对象支持序列化
  7. 00 01: 类内的域个数。

.输出contain的唯一的域描述,int containVersion=11;

  1. 0x49: 域类型. 49 代表"I", 也就是Int..
  2. 00 0E: 域名字长度.
  3. 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, 域名字描述.
  4. 0x78: TC_ENDBLOCKDATA对象块结束的标志.

这时,序列化算法会检查contain是否有超类,如果有的话会接着输出。

  1. 0x70:TC_NULL,没有超类了。

最后,将contain类实际域值输出。

  1. 00 00 00 0B: 11, containVersion的值.

OK,我们讨论了java序列化的机制和原理,希望能对同学们有所帮助。

本文来自longdick的博客:《Java序列化算法透析》


相关文章:

转 --有些事情需要注意

1:能不抽烟最好不抽,它或许可以帮助你吸引一些女生,但不抽绝不会招来厌烦,表现男子气概的途径有很多,没必要拿健康做赌注。2:给自己定目标,一年,两年,五年,也许你出生不如…

《Python数据分析与挖掘实战》一3.1 数据质量分析

本节书摘来自华章出版社《Python数据分析与挖掘实战》一书中的第3章,第3.1节,作者 张良均 王路 谭立云 苏剑林,更多章节内容可以访问云栖社区“华章计算机”公众号查看 3.1 数据质量分析 数据质量分析是数据挖掘中数据准备过程的重要一环&am…

Matlab与线性代数 -- 矩阵的右除

打磨一项技能最需要的就是耐心,我们知道做一件事情不会一蹴而就,需要长时间的积累。关于Matlab的打磨会持续很长的时间,每天学习一个知识点,一年下来就不得了。要有耐心,要有耐心,跟着我们每天花5分钟的时间…

Java中的值传递和引用传递

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参…

ASP.NET网站建设基本常用代码

1.为按钮添加确认对话框Button.Attributes.Add("onclick","return confirm(确认?)");Button.Attributes.Add("onclick","if(confirm(确定?)){return true;}else{return false;}") 2.表格超连接列传递参数<asp:HyperLinkColumn Ta…

C#语言与面向对象技术(6)

本图文主要掌握以下问题&#xff1a; 1.什么是“类型安全”问题&#xff1f; 2.为什么要引入泛型&#xff1f; 3.什么是泛型&#xff1f; 4.泛型是如何实现的&#xff1f; 5.类与类之间存在哪些关系&#xff0c;如何表示&#xff1f;

Xposed: 勾住(Hook) Android应用程序对象的方法,实现AOP

Xposed Xposed能够勾住(Hook) Android应用程序对象的方法&#xff0c;实现AOP&#xff0c;一个简单的例子&#xff1a; public class WebViewHook implements IXposedHookLoadPackage {// handleLoadPackage 会在android加载每一个apk后执行public void handleLoadPackage(Load…

Servlet防止页面被客户端缓存

服务器端的HttpServlet可通过设置特定HTTP响应头来禁止客户端缓存网页&#xff0c;以下示范代码中的response变量引用HttpServletResponse对象&#xff1a; response.addHeader("Pragma","no-cache"); response.setHeader("Cache-Control","…

二进制存储图片

二进制存储图片 如果我们要将一个图片文件二进制于数据库中&#xff0c;那么我们就必须将图片文件转化为二进制数据内容&#xff0c;再将二进制数据存储至数据库中&#xff0c;这是图片存储&#xff08;或是其它文件数据库存储&#xff09;的基本原则。 至于要从数据库中读取图…

《HTML5开发手册》——2.4 初学者“菜谱”:使用address元素提供通信信息

本节书摘来自异步社区《HTML5开发手册》一书中的第2章&#xff0c;第2.4节,作者&#xff1a; 【美】Chuck Hudson , 【英】Tom Leadbetter 更多章节内容可以访问云栖社区“异步社区”公众号查看。 2.4 初学者“菜谱”&#xff1a;使用address元素提供通信信息 规范中将address…

Matlab与线性代数 -- 矩阵的转置

打磨一项技能最需要的就是耐心&#xff0c;我们知道做一件事情不会一蹴而就&#xff0c;需要长时间的积累。关于Matlab的打磨会持续很长的时间&#xff0c;每天学习一个知识点&#xff0c;一年下来就不得了。要有耐心&#xff0c;要有耐心&#xff0c;跟着我们每天花5分钟的时间…

做为程序员对sql进行的性能优化

今天面试&#xff0c;我简历上写了熟悉sql的性能优化&#xff0c;但是今天面试&#xff0c;一时想不起别的&#xff0c;就仅仅说出了一条&#xff0c;在这里再总结一些&#xff0c;完善自己的知识点。 我经常用的数据库是oracle&#xff0c;所以我的sql优化是程序员针对于orac…

asp.NET自定义服务器控件内部细节系列教程四

如大家要转载&#xff0c;请保留本人的版权:/* *Description:asp.NET自定义服务器控件内部细节系列教程*Auther:崇崇-天真的好蓝 *MSN:chongchong2008msn.com *Dates:2007-05-20*Copyright:ChongChong2008 YiChang HuBei China */四 服务器控件相关元数据Attribute 1.设计期A…

《C++游戏编程入门(第4版)》——1.12 习题

本节书摘来自异步社区出版社《C游戏编程入门&#xff08;第4版&#xff09;》一书中的第1章&#xff0c;第1.1节&#xff0c;作者&#xff1a;【美】Michael Dawson&#xff08;道森&#xff09;&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看。 1.12 习题 C…

Matlab与线性代数 -- 单位矩阵

打磨一项技能最需要的就是耐心&#xff0c;我们知道做一件事情不会一蹴而就&#xff0c;需要长时间的积累。关于Matlab的打磨会持续很长的时间&#xff0c;每天学习一个知识点&#xff0c;一年下来就不得了。要有耐心&#xff0c;要有耐心&#xff0c;跟着我们每天花5分钟的时间…

语句覆盖(Statement coverage)

一、语句覆盖(Statement coverage)“语句覆盖”是一个比较弱的测试标准&#xff0c;它的含义是&#xff1a;选择足够的测试用例&#xff0c;使得程序中每个语句至少都能被执行一次。 图6.4是一个被测试的程序&#xff0c;它的源程序…

RSS原理和实现

RSS是在互联网上被广泛采用的内容包装和投递协议。网络用户可以在客户端借助于支持RSS的新闻工具软件&#xff0c;在不打开网站内容页面的情况下&#xff0c;阅读支持RSS输出的网站内容。 1.RSS文件结构 示例&#xff1a; <?xml version"1.0" encoding"gb23…

consul安装配置使用

2019独角兽企业重金招聘Python工程师标准>>> 环境 centos:7.3 docker:1.12.6 kernel:3.10.0-514.6.1.el7.x86_64 consul:0.8.1 server1:10.1.13.221 server2:10.1.13.222 consul的功能 服务发现 健康检查 支持多数据中心 key/value存储 consul的使用场景 docker实例…

Matlab与线性代数 -- 全1矩阵

打磨一项技能最需要的就是耐心&#xff0c;我们知道做一件事情不会一蹴而就&#xff0c;需要长时间的积累。关于Matlab的打磨会持续很长的时间&#xff0c;每天学习一个知识点&#xff0c;一年下来就不得了。要有耐心&#xff0c;要有耐心&#xff0c;跟着我们每天花5分钟的时间…

java 冒泡排序和快速排序 实现

面试的时候经常会遇到面试官让你直接手写排序算法&#xff0c;下面是冒泡排序和快速排序的实现。冒泡排序基本流程就是&#xff0c;自下而上比较相邻的两个元素进行比较&#xff0c;让大的元素往下面沉&#xff0c;较小的往上冒。按照排序规则进行比较&#xff0c;如果是跟排序…

Matlab与线性代数 -- 零矩阵

打磨一项技能最需要的就是耐心&#xff0c;我们知道做一件事情不会一蹴而就&#xff0c;需要长时间的积累。关于Matlab的打磨会持续很长的时间&#xff0c;每天学习一个知识点&#xff0c;一年下来就不得了。要有耐心&#xff0c;要有耐心&#xff0c;跟着我们每天花5分钟的时间…

全球15个顶级技术类博客

1) 生活骇客&#xff08;Lifehacker&#xff09; http://www.lifehacker.com 生活骇客&#xff08;Lifehacker&#xff09;的座右铭表达了它的全部理念&#xff1a;“不要为技术而生活&#xff0c;要为生活而关注技术&#xff01;”这个博客提供了有关于各方各面的“时间节省”…

[ExtJS5学习笔记]第五节 使用fontawesome给你的extjs5应用添加字体图标

本文地址&#xff1a;http://blog.csdn.net/sushengmiyan/article/details/38458411本文作者&#xff1a;sushengmiyan-------------------------------------------------资源链接--------------------------------------------------------FontAwesome glyph编码&#xff1a;…

正则式高人谈解答正则式的心得

条件1&#xff1a; 长度为14个字符 条件2&#xff1a; 其中任意9个位置为数字&#xff0c;并且数字只能是(0,1,3) 条件3&#xff1a; 其余的位置全部为"-"符号 ------------------------------------------ 求一个正则表达式 答案为&#xff1a;^(?!(.*?-){6,})(?…

数据结构与算法--线性表(顺序表)

本图文主要掌握以下问题&#xff1a; 1. 什么是线性表&#xff0c;线性表有哪些操作&#xff1f; 2. 如何利用顺序结构实现线性表&#xff1f;

Myeclipse在启动tomcat的时候的模式改变

在Myeclipse中&#xff0c; windows->preferences->Myeclipse->Servers->Tomcat 然后找到你的相应的Tomcat服务器的版本 当选择Debug mode的时候&#xff0c;当启动tomcat的时候&#xff0c;会进入debug视图 当选择Run mode的时候&#xff0c;启动tomcat的时候&a…

Request.ServerVariables参数集

Request.ServerVariables("Url") 返回服务器地址 Request.ServerVariables("Path_Info") 客户端提供的路径信息 Request.ServerVariables("Appl_Physical_Path") 与应用程序元数据库路径相应的物理路径 Request.ServerVariables("Path_T…

Linux (x86) Exploit 开发系列教程之十一 Off-By-One 漏洞(基于堆)

Off-By-One 漏洞&#xff08;基于堆&#xff09; 译者&#xff1a;飞龙 原文&#xff1a;Off-By-One Vulnerability (Heap Based) 预备条件&#xff1a; Off-By-One 漏洞&#xff08;基于栈&#xff09;理解 glibc mallocVM 配置&#xff1a;Fedora 20&#xff08;x86&#xff…

利用链式存储结构实现线性表

本图文主要介绍了如何利用链式存储结构实现线性表。