java 序列化的一系列问题

什么是序列化,为什么序列化,怎么序列化,序列化可能会遇到什么问题,如何解决?

 

一、什么是序列化

序列化分为两大部分:序列化和反序列化。

序列化就是将对象或者数据结构转化成特定的格式,使其可在网络中传输,或者可存储在内存或者文件中。

反序列化则是相反的操作,将对象从序列化数据中还原出来。

二、为什么序列化

序列化可以将对象状态转换为可保持或传输的形式,从而可以更好地保存、传输。

把内存中的对象保存到一个文件中或者数据库中时候,需要把对象序列化成为字节码,再保存起来。用套接字传输对象的时候,需要把对象序列化成为字节码,传输之后,再反序列化将对象还原。

这里有两个使用场景:

(1)信息记录

有一个类专门记录用户信息设置。当程序退出后下次再运行要保留上次的信息设置,那就可以把这个类的对象进行序列化,作为配置文件存在磁盘上,每次运行的时候再读取。

(2)信息传输

传输服务,需要把用户数据从服务器端传输给用户,这时就可以使用序列化。

但是我们也可以想想,这两种场景真的有必要使用序列化吗?

在第一个使用场景,用户信息完全可以使用配置文件采用键值对的方式来实现。但是使用配置文件意味着用户可以自己修改(就像很久以前的pc游戏,可以自己改配置文件修改数值),使用序列化的方式可以让配置文件可读性变差,更加安全(?)。

在第二个使用场景,完全可以使用json、protobuf的方式来传,就是需要把要传输的数据封装成json和protobuf的格式,和序列化的原理没有什么区别。

第一个场景我也许会使用序列化,但是第二个场景似乎就没有必要了,我个人不推荐使用。具体情况还需具体分析。

三、怎么序列化

1、基本使用

Xie.java

Test.java

2、serialVersionUID

需要特别注意serialVersionUID这个字段。就算两个类的功能代码完全一致,只要serialVersionUID不同,就无法相互序列化和反序列化。

使用上面的例子:

首先我把serialVersionUID设为1L,将其序列化到文件中,此时反序列化可以成功。

然后,我把serialVersionUID设为2L,直接进行反序列化,此时报错:

如果服务端想要禁止某些客户端的数据交互,就可以更改serialVersionUID,让客户端无法进行反序列化,从而无法与获得服务端的数据。

同时,可以授权某些客户端更换serialVersionUID,这些客户端就不会受到影响。

3、静态变量序列化的问题

在Xie.java中加上一个静态变量staticVar:

将其序列化。再去掉staticVar,进行反序列化,可以正常输出结果。

这可以说明,静态变量不会影响对象的序列化和反序列化。

其实比较容易理解,序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。

4、transient关键字

如果在Xie.java中的hobby变量加上transient进行修饰:

反序列化后,得出的结果为:

这可以说明hobby变量没有被序列化。

transient关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient变量的值被设为初始值,如int型的是0,对象型的是null。

四、序列化中可能遇到的问题

个人认为是安全问题。

参考:https://www.zhihu.com/question/37562657

安全问题中比较重要的就是确保数据来源的安全性和对敏感数据的保护。Java的序列化的使用恰好涉及到了以上两个领域。

Java对象序列化是JDK1.1中引入的一组开创性特性之一,用于作为一种将Java对象的状态转换为字节数组,以便存储或传输的机制,以后,仍可以将字节数组转换回Java对象原有的状态。

这种特性给我们带来了方便,但是也给我们带来了危险。如果我们传输的序列化数据中途被截获,截获方通过反序列化就可以获得里面的数据(敏感数据的泄露),甚至对里面的数据进行修改然后发送给接收方(无法确保数据来源的安全性)。

那么要怎么安全的使用序列化呢?

(1)避免传递敏感数据

在对象中,可以使用关键字transient修饰敏感数据的变量,这样它就不会写入到序列化流中。

(2)对数据进行先签名后加密的策略(重要)

如果非要传递敏感数据,那就使用这个方法,别人也就无法解密以及伪造。

其实安全问题已经是非常广泛的问题了。任何时候都要有安全意识。

五、总结

不需要刻意使用序列化,但是要有一定的理解。

发表评论

电子邮件地址不会被公开。 必填项已用*标注