Serializable接口
Serializable接口:
一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化
一、什么是序列化?
【将对象的状态信息转换为可以存储或传输的形式的过程,在序列化期间,对象将其当前状态写入到临时存储区或持久性存储区,之后,便可以通过从存储区中读取或反序列化对象的状态信息,来重新创建该对象】
序列化将数据分解成字节流,以便存储在文件中或在网络上传输。
反序列化就是打开字节流并重构对象。
二、什么情况下需要序列化?
【当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化】
三、Serializable主要用来支持两种主要的特性:
1、Java的RMI(remote method invocation),RMI允许像在本机上一样操作远程机器上的对象,当发送消息给远程对象时,就需要用到序列化机制来发送参数和接受返回值。
2、Java的JavaBean,Bean的状态信息通常是在设计时配置的,Bean的状态信息必须被保存下来,以便当程序运行时能恢复这些状态信息,这也需要序Serializable机制。
只需要了解被序列化的类需要实现 Serializable 接口,使用 ObjectInputStream 和 ObjectOutputStream 进行对象的读写。
四、常出现的情景
1、两个类A B代码完全相同,试图通过网络传递对象数据,A 端将对象 C 序列化为二进制数据再传给 B,B 反序列化得到 C。但一直反序列化不成功。
解决:虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。虽然两个类的功能代码完全一致,但是序列化 ID 不同,他们无法相互序列化和反序列化。
!!!!!!
序列化 ID 在 Eclipse 下提供了两种生成策略,
(1)固定的 1L(一般这么做。可以确保代码一致时反序列化成功)
private static final long serialVersionUID = 1L;
(2)一个是随机生成一个不重复的 long 类型数据(作用是:通过改变序列化 ID 可以用来限制某些用户的使用。)
2、Facade外观模式:
Client 端通过 Façade Object 才可以与业务逻辑对象进行交互。而客户端的 Façade Object 不能直接由 Client 生成,而是需要 Server 端生成,然后序列化后通过网络将二进制对象数据传给 Client,Client 负责反序列化得到 Façade 对象。
即 服务端(生成Facade object)序列化 ----->客户端(反序列化,得到Facade对象,可用其与业务逻辑对象交互)
该模式可以使得 Client 端程序的使用需要服务器端的许可,同时 Client 端和服务器端的 Façade Object 类需要保持一致。当服务器端想要进行版本更新时,只要将服务器端的 Façade Object 类的序列化 ID 再次生成,当 Client 端反序列化 Façade Object 就会失败,也就是强制 Client 端从服务器端获取最新程序。
3、一个类实现了序列化,并且有其静态变量。
但序列化保存的是对象的状态,静态变量属于类的状态,因此 序列化并不保存静态变量。
4、父类的序列化与transient:
父类不实现序列化,子类实现序列化,则将子类对象反序列化后输出父类定义的某变量的数值,该变量数值与序列化时的数值不同。
原因:一个java对象的构造必须先有父对象,再有子对象,反序列化也是,父类没有实现序列化,则虚拟机不会序列化父类。
解决:
a、父类也实现序列化
b、为父类创造默认的无参的构造方法,在父类无参构造方法中对父类进行变量初始化。则反序列化时,构造父对象时只能调用父类无参构造函数为默认父对象,如果不先在无参构造函数中初始化变量,则父类变量值都会是默认声明值,如0,null。
所以,使变量不被序列化的方法有:
a、Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
b、可以将不需要被序列化的字段抽取出来放到父类中,子类实现序列化,父类不实现,则父类的字段数据将不会被序列化。
5、对敏感字段加密
服务端给客户端发送序列化对象数据,并在序列化时进行加密,客户端用解密密钥,在反序列化时,对密码读取,保证了序列化对象的数据安全。
在序列化过程中,虚拟机会试图调用对象类里的 writeObject 和 readObject 方法,进行用户自定义的序列化和反序列化,如果没有这样的方法,则默认调用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。用户自定义的 writeObject 和 readObject 方法可以允许用户控制序列化的过程,比如可以在序列化的过程中动态改变序列化的数值。基于这个原理,可以在实际应用中得到使用,用于敏感字段的加密工作。
如RMI技术。