5
5
- Java基础
6
6
---
7
7
8
- ## 序列化和反序列化相关概念
9
-
10
- ### 什么是序列化?什么是反序列化?
8
+ ## 什么是序列化和反序列化?
11
9
12
10
如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网络传输 Java 对象,这些场景都需要用到序列化。
13
11
18
16
19
17
对于 Java 这种面向对象编程语言来说,我们序列化的都是对象(Object)也就是实例化后的类(Class),但是在 C++这种半面向对象的语言中,struct(结构体)定义的是数据结构类型,而 class 对应的是对象类型。
20
18
19
+ 下面是序列化和反序列化常见应用场景:
20
+
21
+ - 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
22
+ - 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
23
+ - 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
24
+ - 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。
25
+
21
26
维基百科是如是介绍序列化的:
22
27
23
28
> ** 序列化** (serialization)在计算机科学的数据处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。对于许多对象,像是使用大量引用的复杂对象,这种序列化重建的过程并不容易。面向对象中的对象序列化,并不概括之前原始对象所关系的函数。这种过程也称为对象编组(marshalling)。从一系列字节提取数据结构的反向操作,是反序列化(也称为解编组、deserialization、unmarshalling)。
28
33
29
34
<p style =" text-align :right ;font-size :13px ;color :gray " >https://www.corejavaguru.com/java/serialization/interview-questions-1</p >
30
35
31
- ### 实际开发中有哪些用到序列化和反序列化的场景?
32
-
33
- 1 . 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
34
- 2 . 将对象存储到文件中的时候需要进行序列化,将对象从文件中读取出来需要进行反序列化。
35
- 3 . 将对象存储到缓存数据库(如 Redis)时需要用到序列化,将对象从缓存数据库中读取出来需要反序列化。
36
-
37
- ### 序列化协议对应于 TCP/IP 4 层模型的哪一层?
36
+ ** 序列化协议对应于 TCP/IP 4 层模型的哪一层?**
38
37
39
38
我们知道网络通信的双方必须要采用和遵守相同的协议。TCP/IP 四层模型是下面这样的,序列化协议属于哪一层呢?
40
39
41
- 1 . 网络接口层
42
- 2 . 网络层
43
- 3 . 传输层
44
- 4 . 应用层
40
+ 1 . 应用层
41
+ 2 . 传输层
42
+ 3 . 网络层
43
+ 4 . 网络接口层
45
44
46
- ![ TCP/IP 4层模型 ] ( https://my -blog-to-use .oss-cn-beijing .aliyuncs.com/2020-8/6ecb84cd-4227-4c7b-a2e8-b77054604400-20200802201216504 .png )
45
+ ![ TCP/IP 四层模型 ] ( https://guide -blog-images .oss-cn-shenzhen .aliyuncs.com/github/javaguide/cs-basics/network/tcp-ip-4-model .png )
47
46
48
47
如上图所示,OSI 七层协议模型中,表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。反过来的话,就是将二进制流转换成应用层的用户数据。这不就对应的是序列化和反序列化么?
49
48
50
49
因为,OSI 七层协议模型中的应用层、表示层和会话层对应的都是 TCP/IP 四层模型中的应用层,所以序列化协议属于 TCP/IP 协议应用层的一部分。
51
50
52
- ## 常见序列化协议对比
51
+ ## 常见序列化协议有哪些?
53
52
54
- JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且部分版本有安全漏洞 。比较常用的序列化协议有 hessian、kryo、protostuff 。
53
+ JDK 自带的序列化方式一般不会用 ,因为序列化效率低并且存在安全问题 。比较常用的序列化协议有 Hessian、Kryo、Protobuf、ProtoStuff,这些都是基于二进制的序列化协议 。
55
54
56
- 下面提到的都是基于二进制的序列化协议, 像 JSON 和 XML 这种属于文本类序列化方式。虽然 JSON 和 XML 可读性比较好 ,但是性能较差,一般不会选择。
55
+ 像 JSON 和 XML 这种属于文本类序列化方式。虽然可读性比较好 ,但是性能较差,一般不会选择。
57
56
58
57
### JDK 自带的序列化方式
59
58
@@ -76,12 +75,33 @@ public class RpcRequest implements Serializable {
76
75
}
77
76
```
78
77
79
- > 序列化号 serialVersionUID 属于版本控制的作用。序列化的时候 serialVersionUID 也会被写入二进制序列,当反序列化时会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致。如果 serialVersionUID 不一致则会抛出 ` InvalidClassException ` 异常。强烈推荐每个序列化类都手动指定其 ` serialVersionUID ` ,如果不手动指定,那么编译器会动态生成默认的序列化号
78
+ ** serialVersionUID 有什么作用?**
79
+
80
+ 序列化号 ` serialVersionUID ` 属于版本控制的作用。反序列化时,会检查 ` serialVersionUID ` 是否和当前类的 ` serialVersionUID ` 一致。如果 ` serialVersionUID ` 不一致则会抛出 ` InvalidClassException ` 异常。强烈推荐每个序列化类都手动指定其 ` serialVersionUID ` ,如果不手动指定,那么编译器会动态生成默认的 ` serialVersionUID ` 。
81
+
82
+ ** serialVersionUID 不是被 static 变量修饰了吗?为什么还会被“序列化”?**
83
+
84
+ ` static ` 修饰的变量是静态变量,位于方法区,本身是不会被序列化的。 ` static ` 变量是属于类的而不是对象。你反序列之后,` static ` 变量的值就像是默认赋予给了对象一样,看着就像是 ` static ` 变量被序列化,实际只是假象罢了。
80
85
81
- 我们很少或者说几乎不会直接使用这个序列化方式,主要原因有两个:
86
+ ** 如果有些字段不想进行序列化怎么办? **
82
87
83
- 1 . ** 不支持跨语言调用** : 如果调用的是其他语言开发的服务的时候就不支持了。
84
- 2 . ** 性能差** :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。
88
+ 对于不想进行序列化的变量,可以使用 ` transient ` 关键字修饰。
89
+
90
+ ` transient ` 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 ` transient ` 修饰的变量值不会被持久化和恢复。
91
+
92
+ 关于 ` transient ` 还有几点注意:
93
+
94
+ - ` transient ` 只能修饰变量,不能修饰类和方法。
95
+ - ` transient ` 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 ` int ` 类型,那么反序列后结果就是 ` 0 ` 。
96
+ - ` static ` 变量因为不属于任何对象(Object),所以无论有没有 ` transient ` 关键字修饰,均不会被序列化。
97
+
98
+ ** 为什么不推荐使用 JDK 自带的序列化?**
99
+
100
+ 我们很少或者说几乎不会直接使用 JDK 自带的序列化方式,主要原因有下面这些原因:
101
+
102
+ - ** 不支持跨语言调用** : 如果调用的是其他语言开发的服务的时候就不支持了。
103
+ - ** 性能差** :相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。
104
+ - ** 存在安全问题** :序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。相关阅读:[ 应用安全: JAVA 反序列化漏洞之殇 - Cryin] ( https://cryin.github.io/blog/secure-development-java-deserialization-vulnerability/ ) 、[ Java反序列化安全漏洞怎么回事? - Monica] ( https://www.zhihu.com/question/37562657/answer/1916596031 ) 。
85
105
86
106
### Kryo
87
107
@@ -174,13 +194,13 @@ protostuff 基于 Google protobuf,但是提供了更多的功能和更简易
174
194
175
195
Github 地址:[ https://github.com/protostuff/protostuff ] ( https://github.com/protostuff/protostuff ) 。
176
196
177
- ### hessian
197
+ ### Hessian
178
198
179
- hessian 是一个轻量级的,自定义描述的二进制 RPC 协议。hessian 是一个比较老的序列化实现了,并且同样也是跨语言的。
199
+ Hessian 是一个轻量级的,自定义描述的二进制 RPC 协议。Hessian 是一个比较老的序列化实现了,并且同样也是跨语言的。
180
200
181
201
![ ] ( https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2020-8/8613ec4c-bde5-47bf-897e-99e0f90b9fa3.png )
182
202
183
- dubbo RPC 默认启用的序列化方式是 hessian2 ,但是,Dubbo 对 hessian2 进行了修改,不过大体结构还是差不多。
203
+ Dubbo2.x 默认启用的序列化方式是 Hessian2 ,但是,Dubbo 对 Hessian2 进行了修改,不过大体结构还是差不多。
184
204
185
205
### 总结
186
206
@@ -191,8 +211,3 @@ Kryo 是专门针对 Java 语言序列化方式并且性能非常好,如果你
191
211
像 Protobuf、 ProtoStuff、hessian 这类都是跨语言的序列化方式,如果有跨语言需求的话可以考虑使用。
192
212
193
213
除了我上面介绍到的序列化方式的话,还有像 Thrift,Avro 这些。
194
-
195
- ## 其他推荐阅读
196
-
197
- - 美团技术团队-序列化和反序列化:[ https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.html ] ( https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.html )
198
- - 在 Dubbo 中使用高效的 Java 序列化(Kryo 和 FST): [ https://dubbo.apache.org/zh/docs/v3.0/references/serializations/serialization/ ] ( https://dubbo.apache.org/zh/docs/v3.0/references/serializations/serialization/ )
0 commit comments