`
shaomeng95
  • 浏览: 223160 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java序列化算法透析

 
阅读更多

转载http://longdick.iteye.com

Java 序列化算法透析

 

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

 

序列化的必要性

 

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

 

如何序列化一个对象

 

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

 

Java代码 复制代码 收藏代码
  1. import java.io.Serializable;   
  2.   
  3. class TestSerial implements Serializable {   
  4.   
  5.        public byte version = 100;   
  6.   
  7.        public byte count = 0;   
  8.   
  9. }  
import java.io.Serializable;

class TestSerial implements Serializable {

       public byte version = 100;

       public byte count = 0;

}

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

  

Java代码 复制代码 收藏代码
  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.   
  15. }  
public static void main(String args[]) throws IOException {

       FileOutputStream fos = new FileOutputStream("temp.out");

       ObjectOutputStream oos = new ObjectOutputStream(fos);

       TestSerial ts = new TestSerial();

       oos.writeObject(ts);

       oos.flush();

       oos.close();

}

 

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

 

Java代码 复制代码 收藏代码
  1. public static void main(String args[]) throws IOException {   
  2.   
  3.        FileInputStream fis = new FileInputStream("temp.out");   

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

       FileInputStream fis = new FileInputStream("temp.out");

       ObjectInputStream oin = new ObjectInputStream(fis);

       TestSerial ts = (TestSerial) oin.readObject();

       System.out.println("version="+ts.version);

}

 

 执行结果为

100.

 

对象的序列化格式

 

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

Xml代码 复制代码 收藏代码
  1. AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65   
  2.   
  3. 73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05   
  4.   
  5. 63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78   
  6.   
  7. 70 00 64  
AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65

73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05

63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78

70 00 64

 

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

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

public byte version = 100;

public byte count = 0;

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

 

Java 的序列化算法

 

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

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

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

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

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

 

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

Java代码 复制代码 收藏代码
  1. class parent implements Serializable {   
  2.   
  3.        int parentVersion = 10;   

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

       int parentVersion = 10;

}

 

class contain implements Serializable{

       int containVersion = 11;

}

public class SerialTest extends parent implements Serializable {

       int version = 66;

       contain con = new contain();

 

       public int getVersion() {

              return version;

       }

       public static void main(String args[]) throws IOException {

              FileOutputStream fos = new FileOutputStream("temp.out");

              ObjectOutputStream oos = new ObjectOutputStream(fos);

              SerialTest st = new SerialTest();

              oos.writeObject(st);

              oos.flush();

              oos.close();

       }

}

 

这个例子是相当的直白啦。 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

 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

还有 SerialTest 类的域:

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

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

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

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

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

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

  • 0x70:TC_NULL ,没有超类了。

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

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

 

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

 

参考资料:

http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf

http://www.javaworld.com/community/node/2915

分享到:
评论

相关推荐

    java序列化原理与算法

    #### Java序列化算法透析 Java序列化的核心是将对象转换为字节流,这涉及到一系列复杂的操作步骤。 ##### 序列化算法示例 为了更好地理解序列化的过程,我们可以通过一个简单的例子来进行说明。 ```java import ...

    避开10大常见坑:DeepSeekAPI集成中的错误处理与调试指南.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    前端分析-2023071100789

    前端分析-2023071100789

    基于kinect的3D人体建模C++完整代码.cpp

    基于kinect的3D人体建模C++完整代码.cpp

    搞机工具箱10.1.0.7z

    搞机工具箱10.1.0.7z

    GRU+informer时间序列预测(Python完整源码和数据)

    GRU+informer时间序列预测(Python完整源码和数据),python代码,pytorch架构,适合各种时间序列直接预测。 适合小白,注释清楚,都能看懂。功能如下: 代码基于数据集划分为训练集测试集。 1.多变量输入,单变量输出/可改多输出 2.多时间步预测,单时间步预测 3.评价指标:R方 RMSE MAE MAPE,对比图 4.数据从excel/csv文件中读取,直接替换即可。 5.结果保存到文本中,可以后续处理。 代码带数据,注释清晰,直接一键运行即可,适合新手小白。

    性价比革命:DeepSeekAPI成本仅为GPT-4的3%的技术揭秘.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    基于ANSYS LSDyna的DEM-SPH-FEM耦合模拟滑坡入水动态行为研究,基于ANSYS LSDyna的DEM-SPH-FEM耦合的滑坡入水模拟分析研究,基于ansys lsdyna的滑坡入水

    基于ANSYS LSDyna的DEM-SPH-FEM耦合模拟滑坡入水动态行为研究,基于ANSYS LSDyna的DEM-SPH-FEM耦合的滑坡入水模拟分析研究,基于ansys lsdyna的滑坡入水模拟dem-sph-fem耦合 ,基于ANSYS LSDyna; 滑坡入水模拟; DEM-SPH-FEM 耦合,基于DEM-SPH-FEM耦合的ANSYS LSDyna滑坡入水模拟

    auto_gptq-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

    auto_gptq-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

    复件 复件 建设工程可行性研究合同[示范文本].doc

    复件 复件 建设工程可行性研究合同[示范文本].doc

    13考试真题最近的t64.txt

    13考试真题最近的t64.txt

    Microsoft Visual C++ 2005 SP1 Redistributable PackageX86

    好用我已经解决报错问题

    嵌入式开发入门:用C语言点亮LED灯的全栈开发指南.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

    auto_gptq-0.4.2-cp38-cp38-win_amd64.whl

    auto_gptq-0.4.2-cp38-cp38-win_amd64.whl

    自动立体库设计方案.pptx

    自动立体库设计方案.pptx

    手把手教你用C语言实现贪吃蛇游戏:从算法设计到图形渲染.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

    性能对决:DeepSeek-V3与ChatGPTAPI在数学推理场景的基准测试.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    从零到一:手把手教你用Python调用DeepSeekAPI的完整指南.pdf

    在日常的工作和学习中,你是否常常为处理复杂的数据、生成高质量的文本或者进行精准的图像识别而烦恼?DeepSeek 或许就是你一直在寻找的解决方案!它以其高效、智能的特点,在各个行业都展现出了巨大的应用价值。然而,想要充分发挥 DeepSeek 的优势,掌握从入门到精通的知识和技能至关重要。本文将从实际应用的角度出发,为你详细介绍 DeepSeek 的基本原理、操作方法以及高级技巧。通过系统的学习,你将能够轻松地运用 DeepSeek 解决实际问题,提升工作效率和质量,让自己在职场和学术领域脱颖而出。现在,就让我们一起开启这场实用又高效的学习之旅吧!

    为什么你的switch总出bug?90%新手不知道的break语句隐藏规则.pdf

    # 踏入C语言的奇妙编程世界 在编程的广阔宇宙中,C语言宛如一颗璀璨恒星,以其独特魅力与强大功能,始终占据着不可替代的地位。无论你是编程小白,还是有一定基础想进一步提升的开发者,C语言都值得深入探索。 C语言的高效性与可移植性令人瞩目。它能直接操控硬件,执行速度快,是系统软件、嵌入式开发的首选。同时,代码可在不同操作系统和硬件平台间轻松移植,极大节省开发成本。 学习C语言,能让你深入理解计算机底层原理,培养逻辑思维和问题解决能力。掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情遨游,挖掘无限可能,为未来的编程之路打下坚实基础!

Global site tag (gtag.js) - Google Analytics