Java 序列化深入分析Word文档下载推荐.docx
《Java 序列化深入分析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Java 序列化深入分析Word文档下载推荐.docx(13页珍藏版)》请在冰豆网上搜索。
![Java 序列化深入分析Word文档下载推荐.docx](https://file1.bdocx.com/fileroot1/2022-11/15/19e53f74-1c9b-41ce-9a4f-57d142fb4098/19e53f74-1c9b-41ce-9a4f-57d142fb40981.gif)
Serializable接口
Serializable接口在JDK源码中定义如下:
/*
*@authorunascribed
*@seejava.io.ObjectOutputStream
*@seejava.io.ObjectInputStream
*@seejava.io.ObjectOutput
*@seejava.io.ObjectInput
*@seejava.io.Externalizable
*@sinceJDK1.1
*/
publicinterfaceSerializable{
}
Java从JDK1.1开始支持对象的序列化机制,有上图的源码可知,Serializable接口没有声明任何方法,实现该接口的Java类不需要对任何方法提供实现(默认情况下,定制序列化时除外),**因此,该接口仅仅是一个markinterface(标记接口)”,实现该接口意味着告知JVM该对象可以序列化。
**Java序列化机制要求所有具备序列化的对象必须实现该接口,否则是不能被序列化的,如果对于没有实现该接口的对象进行序列化时,JavaAPI会抛出异常,无法进行序列化。
transient关键字
瞬时变量,指的是被transient关键字修饰的变量,该关键字表示为瞬时的,即不做持久化处理的,以此来控制属性是否被包含进入序列化的字节流中。
Serializable接口提供了默认的序列化行为,在默认情况下,开发人员只需实现该接口,无需进行其他额外的操作,即可实现的对象的序列化。
默认只对对象中非静态的字段以及非瞬时的字段进行序列化,其他的字段是不允许被序列化的。
这是因为,静态变量是类变量,属于整个类,并非专属于每个对象实例,因此,不序列化静态变量时合理的。
瞬时变量,指的是被transient关键字修饰的变量,该关键字表示为瞬时的,即不做持久化处理的,以此来控制属性是否被包含进入序列化的字节流中。
因此,在序列化时,排除transient关键字修饰的属性也是合理的。
这种情况的具体表现就是,在序列化的有序字节流中没有保存不能被序列化的字段的状态,因此,在反序列化时,这些字段状态是不能被重建的。
但是有一点需要注意的是,经过反序列化后的对象,除了对可被序列化的字段状态进行重建之外,其他的没有被序列化的字段作为对象属性的一部分,也在对象重建时得以初始化。
但是这些字段的状态是不被保存的,重建后的这些属性仅仅是系统赋予的默认值,而非保存了对象序列化之前的状态。
实现Serializable接口除了提供默认的序列化方式之外,同样允许开发人员定制序列化,即通过实现以下相同签名的方法来实现:
序列化方法:
privatevoidwriteObject(java.io.ObjectOutputStreamout)throwsIOException
反序列化方法:
privatevoidreadObject(java.io.ObjectInputStreamin)throwsIOException,ClassNotFoundException;
其中,writeObject方法用于定制序列化,readObject方法用于实现定制反序列化。
serialVersionUID
每个实现Serializable接口的对象都有一个serialVersionUID,长整型,64位,唯一标示了该序列化对象。
在类定义中,可以显示的定义该静态变量,也可以不定义。
在不定义的情况下,Java编译器会隐式的生成该变量。
强烈建议显示定义。
那么,该变量有什么用途呢?
反序列化兼容控制,serialVersionUID相同才能进行反序列化。
例如:
远程主机需要反序列化对象C,如果在本地和远程主机内的C对象持有的serialVersionUID不同,即使两个类其它部分完全一样,也是不能成功反序列化话的,会抛出异常。
因此,如果对类做了修改,为了保证版本对序列化兼容,该变量的值保持不变。
从另一个角度来讲,不期望不同版本的类对序列化兼容,则改变该变量值。
序列化保存的数据
那么,到底哪些数据被序列化到了有序字节流中呢?
字节流数据包含了non-static和non-transient类型的成员数据、类名、类签名、可序列化的基类以及类中引用的其他对象。
针对于父类,有几点原则:
1.如果基类实现了Serializable接口,则其子类默认的是可序列化的,不必显示声明;
2.如果基类没有实现Serializable接口,在反序列化时,会调用基类的无参构造方法来重建基类对象,只不过不会保留基类对象状态。
利用Serializable实现序列化的实例。
publicclassUserimplementsSerializable{
privateStringuserName;
privateStringpassWord;
publicUser(StringuserName,StringpassWord){
this.userName=userName;
this.passWord=passWord;
}
publicStringgetUserName(){
returnuserName;
publicvoidsetUserName(StringuserName){
publicStringgetPassWord(){
returnpassWord;
publicvoidsetPassWord(StringpassWord){
@Override
publicStringtoString(){
return"
User{"
+
"
userName='
"
+userName+'
\'
'
passWord='
+passWord+'
'
}'
;
测试代码
importjava.io.*;
/**
*@authorbridge
publicclassClient{
publicstaticvoidmain(String[]args){
//构造一个可以序列化对象
Useruser=newUser("
Bridge"
"
123456"
);
//执行序列化
try{
ObjectOutputStreamout=newObjectOutputStream(
newFileOutputStream("
User.dat"
));
out.writeObject(user);
}catch(IOExceptione){
e.printStackTrace();
//反序列化
ObjectInputStreamin=newObjectInputStream(
newFileInputStream("
UseruserRecover=(User)in.readObject();
System.out.println(userRecover);
}catch(ClassNotFoundExceptione){
运行结果
User{userName='
Bridge'
123456'
Externalizable接口
Externalizable接口继承成自Serializable接口,实现该接口意味着对象本身完全掌控自己的序列化方式。
该接口JDK源码如下:
publicinterfaceExternalizableextendsjava.io.Serializable{
voidwriteExternal(ObjectOutputout)throwsIOException;
voidreadExternal(ObjectInputin)throwsIOException,ClassNotFoundException;
Externalizable接口定义了两个方法:
writeExternal(ObjectOutputout)和readExternal(ObjectInputin)。
write方法用于实现定制序列化,read方法用于实现定制反序列化,
定制序列化
大部分情况下,我们期望对类似于密码等这样的敏感信息进行加密处理,以密文的形式在网络间传输,增强数据的安全性。
但是,通过我们上述的方式进行序列化,默认的处理方式是不能保证密码的密文传输的。
因此,针对此类问题,我们必须能够定制对象的序列化和反序列化过程,只有这样才能将我们的业务逻辑加入其中,以满足实际应用的需要。
基于Serializable接口的定制
定制序列化和反序列化与上述序列化方式的不同在于:
自定义类的实现。
首先,同样,自定义的类要实现Serializable接口,这是序列化处理的前提。
不同的是,在定制序列化时,需要根据我们的实际需要,重写writeObject和readObject方法,完成序列化和反序列化的定制。
示例代码如下:
User.java
importsun.misc.BASE64Decoder;
importsun.misc.BASE64Encoder;
privatevoidwriteObject(ObjectOutputStreamout)throwsIOException{
ObjectOutputStream.PutFieldputField=out.put