Java序列化算法透析

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

序列化的必要性

Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。

这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生。

如何序列化一个对象

一个对象能够序列化的前提是实现Serializable接口,Serializable接口没有方法,更像是个标记。

有了这个标记的Class就能被序列化机制处理。

import java.io.Serializable;

class TestSerial implements Serializable {

public byte version = 100;

public byte count = 0;

}

然后我们写个程序将对象序列化并输出。ObjectOutputStream能把Object输出成Byte流。

我们将Byte流暂时存储到temp.out文件里。

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。

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进制方式显示。内容应该如下:

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的序列化算法

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

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

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

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

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

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

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();

}

}

AC ED: STREAM_MAGIC. 声明使用了序列化协议.

00 05: STREAM_VERSION. 序列化协议版本.

0x73: TC_OBJECT. 声明这是一个新的对象.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: 该类所包含的域个数。0x49: 域类型. 49 代表"I", 也就是Int.

00 07: 域名字的长度.

76 65 72 73 69 6F 6E: version,域名字描述.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,对象数据块结束的标志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: 类中域的个数.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, 说明没有其他超类的标志。.00 00 00 0A: 10, parentVersion域的值.00 00 00 42: 66, version域的值.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: 类内的域个数。0x49: 域类型. 49 代表"I", 也就是Int..

00 0E: 域名字长度.

63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, 域名字描述.

0x78: TC_ENDBLOCKDATA对象块结束的标志.0x70:TC_NULL,没有超类了。00 00 00 0B: 11, containVersion的值.

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

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

序列化算法的第一步就是输出对象相关类的描述。例子所示对象为SerialTest类实例,

因此接下来输出SerialTest类的描述。见颜色:

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

然后,算法输出下一个域,contain con = new contain();这个有点特殊,是个对象。

描述对象类型引用时需要使用JVM的标准对象签名表示法,见颜色:

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

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

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

还有SerialTest类的域:

再往后的bytes比较有意思,算法需要描述contain类的信息,要记住,

现在还没有对contain类进行过描述,见颜色:

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

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

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

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

serialVersionUID值的重要作用

根据上面的分析,可以发现如果一个类可序列化,serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败.

注意问题

如果序列化时代码这样写:

SerialTest st = new SerialTest();

oos.writeObject((parent)st);

会发现序列化的对象依然是SerialTest,如果在分布式环境中用Parent反序列化(调用段不存在SerialTest),会造成ClassNotFoundException.

java序列化原理_Java序列化机制和原理相关推荐

  1. java序列化层数_java序列化详解

    一.java序列化与反序列化 序列化是指把java对象(属性)转换为字节序列的过程 反序列化是指把字节序列恢复为java对象的过程 二.为什么需要序列化和反序列化 首先网络通信都是二进制的形式在传输, ...

  2. Java 数据交换格式反射机制SpringIOC原理分析

    数据交换格式&反射机制&SpringIOC原理分析 什么是数据交换格式? 数据交换格式使用场景 JSON简单使用 什么是JSON? JSON格式的分类 常用JSON解析框架 使用fas ...

  3. java序列化流_java 序列化流与反序列化流

    一 对象序列化流ObjectOutputStream ObjectOutputStream 将Java对象的基本数据类型和图形写入OutputStream.可以使用ObjectInputStream ...

  4. java string 反序列化_java序列化和反序列化

    引语: 平时我们在运行程序的时候,创建的对象都在内存中,当程序停止或者中断了,对象也就不复存在了.如果我们能将对象保存起来,在需要使用它的时候在拿出来使用就好了,并且对象的信息要和我们保存 时的信息一 ...

  5. java 反射代价_Java反射机制

    # 反射 ## 什么是反射 反射是Java提供的动态执行机制, 可以动态加载类, 动态创建对象, 动态访问属性, 动态调用方法.. 静态执行: Java代码经过编译以后就确定的执行次序, 称为静态执行 ...

  6. java 抛出异常效率_Java异常处理机制

    与C++.python,Ruby一样,Java也有一个异常处理机制,当程序出现异常时,系统会自动生成一个Exception对象通知程序去处理.之所以这样设计目的在于使程序有更好的容错性和可读性,实现业 ...

  7. java反射的原理_java反射机制的实现原理

    java反射机制的实现原理 反射机制: 所谓的反射机制就是java语言在运行时拥有一项自观的能力. 通过这种能力可以彻底的了解自身的情况为下一步的动作做准备. 下面具体介绍一下java的反射机制.这里 ...

  8. java 可见_Java可见性机制的原理

    基本概念 1.可见性 当一个线程修改了共享变量时,另一个线程可以读取到这个修改后的值. 2.内存屏障(Memory Barriers) 处理器的一组指令,用于实现对内存操作的顺序限制. 3.缓冲行 C ...

  9. java 操作序列_JAVA序列化操作详解

    一.概述 很多时候我们需要将对象的信息以文件的形式存储到硬盘上以便以后可以恢复使用.我们可以按照一定的格式将变量的值依次写到特定格式的文件中.但有时候我们希望只有我们自己可以读懂它并且修改它,这就是序 ...

最新文章

  1. MySQL解压版安装
  2. VC++ MFC DLL动态链接库编写详解
  3. ButterKnife 8.4.0 @BindView 失败,nullpointerexception
  4. C++Merege Sort合并排序的实现算法(附完整源码)
  5. day_work_02
  6. 【收集】常用网络上的Webservice地址
  7. 前端工程师需要懂的前端面试题(c s s方面)总结(二)
  8. 使用frp实现内网穿透
  9. ubuntu安装nvidia显卡驱动+cuda+cudnn
  10. win10 串口驱动下载链接
  11. VlanIF和Vlan间路由
  12. 0 win10重装partition_如何重装win 10 系统?
  13. 访问html 403 iis,IIS 403 错误详细原因 及解决办法总结
  14. Spring使用纯注解配置事务管理并实现简单的增删查改以及模拟转账功能
  15. 【视频】老外拍的阿里巴巴纪录片,讲述淘宝怎么打败eBay
  16. 小程序开发API之监听加速计wx.startAccelerometer
  17. 隐马尔科夫模型java实现
  18. android画直角坐标系,用Android画个五角星
  19. 美女导游孙洁到欧洲推销中国旅游,携程缘何成了名片?
  20. 超级网搜(SuperSearch) - 3.6版,一次关键字输入,多个引擎同时搜索,提高搜索效率,减少解决问题的时间

热门文章

  1. 涉及上千款,2021 Chrome插件性能影响分析报告新鲜出炉
  2. 因财务造假,贾跃亭被罚 2.41 亿元;华为徐直军重申不造成:但将推子品牌汽车;Gradle 7.0 发布|极客头条...
  3. 影响科学圈的“十大名码”!
  4. 优酷爱奇艺回应被处罚;拼多多主体公司法定代表人变更,黄峥退出董事席位;斗鱼发布开源框架Jupiter | 极客头条
  5. 编程坑太多,Map 集合怎么也有这么多坑?一不小心又踩了好几个!| 原力计划...
  6. 我只是追个直播,结果被拉进大咖们的群面对面群聊……
  7. 只会高中数学运算就能发现算法?Google 开源的 AutoML-Zero 有多厉害
  8. 如何用 Python 快速抓取 Google 搜索?
  9. 抗住 60 亿次攻击,起底阿里云安全的演进之路 | 问底中国 IT 技术演进
  10. 为什么数据库使用有序索引,而程序员却在使用哈希表?