再来认识一下 Java 序列化

在面试中,Java 序列化被问到的几率还是挺高的。所以搜集了 Java 序列化常见的问题,由浅入深的帮助大家进一步学习和理解。

再来认识一下 Java 序列化

序列化基础知识

什么是序列化?

Java 序列化是 JDK 1.1 中引入的特性之一。

总的来说,序列化讲一个 Java 对象所描述的所有内容以文件 IO 的方式 存储传输 的过程。核心作用是对象状态的保存和重建

在这里有两个比较重要的概念:

序列化:把 Java 对象转换为字节码的过程

反序列化:把字节码还原为 Java 对象的过程

再来认识一下 Java 序列化

为什么要序列化 ?

因为 Java 对象是存放在 JVM 的 堆内存 中的,当 JVM 退出的时候,对象也就随之销毁。如果想 持久化 或进行 网络传输 对象数据时,那就必须把对象转为计算机可以识别的字节码。

在以下场景中需要使用到序列化。

持久化数据:文件、数据库、缓存

网络传输:RMI (远程调用 Remote Method Invocation)、RPC

如何实现序列化

在 Java 中,没有关键字可以直接去定义一个所谓的 可持久化 对象。这就需要我们在代码中 显示地 进行序列化和反序列化还原操作。

Serializable 接口

Serializable 接口是一个 标记接口,没有方法或字段。一旦实现了此接口,就标志该类的对象就是可序列化的。

1、定义

再来认识一下 Java 序列化

2、序列化

再来认识一下 Java 序列化

3、反序列化

再来认识一下 Java 序列化

4、结果

再来认识一下 Java 序列化

5、如果不实现 Serializable 接口将无法进行序列化或反序列化

再来认识一下 Java 序列化

Externalizable 接口

Externalizable 继承了 Serializable 接口,还定义了两个抽象方法:writeExternal() 和 readExternal()

如果开发人员使用 Externalizable 来实现序列化和反序列化,必须重写 writeExternal() 和 readExternal() 方法。

因为实现 Externalizable 接口之后,基于 Serializable 接口的默认化序列化机制就会失效。

再来认识一下 Java 序列化

Serializable 和 Externalizable 的区别 Serializable Externalizable
Java 支持比较完整,自动存储必要信息   需要开发人员自己完成  
所有对象由 Java 统一保存,性能较低   开发人员决定哪个对象保存,可以提升速度  
保存时占用空间大,性能差   部分存储,空间占用可能较少,性能相对高  

Java 序列化协议分析

下面这段字节码是保存在本地的字节码文件,接下来准备对这段字节码进行 拆分讲解 (只针对 Serializable)。

以下的字节码定义参考 java.io.ObjectStreamConstants 中的定义,如果有兴趣,找到这个类,里面有详细的定义。

再来认识一下 Java 序列化

JDk 序列化的魔数

aced STREAM_MAGIC 魔数,用于标识当前文件的头部

0005 STREAM_VERSION 序列化协议版本号

描述对象的类型信息

73 TC_OBJECT 表示序列化的是一个普通 Java 对象 (Object 0x73,String 0x74,Array 0x75)

72 TC_CLASSDESC 表示当前的对象的类型信息

0014 表示类名的长度,这段代码中是 0014 换算过来是 20 个字节

7374 6174 6963 4661 6374 6f72 792e 5065 7273 6f6e 表示类名,即 staticFactory.Person

0000 0000 0000 0001 类名后的 8 个字节是一个长整数,即 serialVersionUID = 1L

02 SC_SERIALIZABLE 标识位,说明这个类实现了 Serializable 接口。

对象的字段表

0002 表示这个对象中有 2 个属性

49 即 I 表示 int,说明这是一个 32 位整数

0003 表示属性名的长度,即 3 字节

6167 65 表示属性 age

4c 即 L,表示引用类型,说明这个属性是某个类型的引用

0004 表示属性名的长度,即 4 字节

6e61 6d65 表示属性 name

74 TC_STRING 表示后面是个字符串

0012 表示字符串长度,即 18 字节

4c 6a61 7661 2f6c 616e 672f 5374 7269 6e67 3b 即 Ljava/lang/String

父类的描述信息

78 TC_ENDBLOCKDATA 标志所有的字段类型信息描述结束

70 TC_NULL 代表 null,即没有父类

对象的属性值

0000 001e 初始化后的年龄,转换后即 30

74 TC_STRING 表示后面是个字符串

0005 表示字符串长度为 5

4865 6e72 79 初始化之后的姓名,转换后即 Henry

序列化的特性

在实际应用中,有些时候 不能使用默认序列化机制。比如,希望在序列化过程中忽略掉敏感数据。

本段重点讨论 transient 和 static 之间的区别,并讨论每个关键字的作用。

transient 关键字

当我们的一个字段被声明为 transient 后,默认序列化机制就会忽略掉该字段的内容,不会被保存。

static 关键字

序列化仅对特定的变量产生作用,但 static 修饰的变量并不特定于任何对象。因此,静态变量不会参与序列化。

虽然用关键字可以避免序列化,但是当关键字组合使用的时候,也可能会失效。

transient 和 static 的规则

临时变量在序列化过程中将被忽略。

static 变量不会参与序列化。

如果在声明本身期间对值进行了初始化,则静态变量将被序列化。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpygdz.html