java序列化.docx
- 文档编号:25564878
- 上传时间:2023-06-09
- 格式:DOCX
- 页数:14
- 大小:23.24KB
java序列化.docx
《java序列化.docx》由会员分享,可在线阅读,更多相关《java序列化.docx(14页珍藏版)》请在冰豆网上搜索。
java序列化
专业文献阅读
选题:
Java序列化
学院:
________
专业:
_____________________
姓名:
________
学号:
________
指导教师:
________
学期:
________
摘要
当一个程序停止,内存被回收后,内存中对象也不复存在了,但又想在新的程序中使用之前的对象的时候怎么办?
当在网络上传输对象但是网络两端不共享内存的时候怎么办?
当远程接口调用,两端在各自虚拟机中运行并且内存也不共享,要传参和调用结果的时候又该怎么办?
这些问题都涉及到序列化,但是序列化具体指什么,哪些情况会使用到序列化,序列化算法又怎么实现?
本文接下来就会讨论这些问题。
序列化分为两大部分:
序列化和反序列化。
序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输2.[1]。
反序列化则为序列化的逆过程,将对象还原,即打开字节流并重构对象。
对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。
恢复数据要求有恢复数据的对象实例。
Java中用来实现序列化的类都在java.io包中,常用的类或接口有ObjectOutputStream类和ObjectInputStream类及Serialzable接口。
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Objectobj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
关键词:
Java序列化反序列化原理及算法
目录
专业文献阅读1
一.引言4
二.文献综述4
2.1.书籍类阅读文献4
2.文档资料类阅读4
三.文献阅读结果5
3.1.序列化定义5
3.2.序列化必要性及目的6
3.3.序列化算法实现6
3.3.1.序列化算法解析6
3.3.2.序列化实现7
(1) JDK类库中的序列化API7
(2)实现Serializable接口9
(3)实现Externalizable接口10
(4)序列化类的不同版本的序列化兼容性10
3.4.序列化异常10
3.5.序列化范围11
3.6.transient保护敏感信息11
3.7.序列化的问题11
一.引言
当一个程序停止,内存被回收后,内存中对象也不复存在了,但又想在新的程序中使用之前的对象的时候怎么办?
当在网络上传输对象但是网络两端不共享内存的时候怎么办?
当远程接口调用,两端在各自虚拟机中运行并且内存也不共享,要传参和调用结果的时候又该怎么办?
这些问题都涉及到序列化。
序列化是指将对象实例的状态存储到文件或数据库,或把对象的状态通过网络传到远程的计算机,以便程序在需要时再把数据读出来,重构对象。
在Java、VB.net等多数编程语言中都提供了对象序列化的方法。
VBA中没有解决这个问题,VBA是VB的子集,嵌入office等软件中,VB.net等编程语言中提供的序列化方法可以把对象序列化化为二进制数据,也可以将对象序列化化为XML数据;能实现基本序列化、选择序列化、自定义序列化2[7]。
而Java序列化是将Java对象序列化为二进制文件,Java序列化技术是Java系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现Serializable接口,使用ObjectInputStream和ObjectOutputStream进行对象的读写。
二.文献综述
关于序列化方面的文献比较少,书籍方面我找到两三本里面有提到关于序列化的书籍,简单了看了下,对序列化的描述并不详细。
因此,我另外找了文档或者论坛博文来阅读理解序列化,并调试了下序列化实现的java程序。
主要参考文献如下:
2.1.书籍类阅读文献
[1]疯狂java讲义第2版,李刚,电子工业出版社
[2]Java核心技术卷1(原书第8版),CayS.Horstmann,GaryCornell,机械工业出版
[3]ThinkinginJava第三版,BruceEckel
2.文档资料类阅读
[1]Java序列化PPT课件
[2]DiscoverthesecretsoftheJavaSerializationAPI,Java序列化API的秘密
[3]ImplementingSerializable,理解对象序列化
[4]java序列化Serializable
[5]关于序列化反序列化的问题
[6]序列化算法
[7]VBA序列化方法
三.文献阅读结果
3.1.序列化定义
序列化(Serialization)将对象的状态信息转换为可以存储或传输的形式的过程2.[1]。
在序列化期间,对象将其当前状态写入到临时或持久性存储区。
以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。
序列化使其他代码可以查看或修改那些不序列化便无法访问的对象实例数据。
确切地说,代码执行序列化需要特殊的权限:
即指定SerializationFormatter标志的SecurityPermission。
在默认策略下,通过Internet下载的代码或Intranet代码不会授予该权限;只有本地计算机上的代码才被授予该权限。
通常,对象实例的所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。
这样,能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。
类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。
对于任何可能包含重要的安全性数据的对象,如果可能,应该使该对象不可序列化。
如果它必须为可序列化的,请尝试生成特定字段来保存不可序列化的重要数据。
如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。
在java中,序列化(serialization)机制能够将一个实例对象的状态信息写入到一个字节流中,使其可以通过socket进行传输、或者持久化存储到数据库或文件系统中;然后在需要的时候,可以根据字节流中的信息来重构一个相同的对象1.[1]。
序列化机制在java中有着广泛的应用,EJB、RMI等技术都是以此为基础的。
序列化分为两大部分:
序列化和反序列化。
序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输2.[1]。
反序列化则为序列化的逆过程,将对象还原,即打开字节流并重构对象。
对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。
恢复数据要求有恢复数据的对象实例。
ObjectOutputStream中的序列化过程与字节流连接,包括对象类型和版本信息2.[4]。
反序列化时,JVM用头信息生成对象实例,然后将对象字节流中的数据复制到对象数据成员中。
序列化一般用于以下场景:
1)永久性保存对象,保存对象的字节序列到本地文件或者数据库中
2)通过序列化以字节流的形式使对象在网络中进行传递和接收;
3)通过序列化在进程间传递对象。
Java对象序列化是JDK1.1中引入的一组开创性特性之一,用于作为一种将Java对象的状态转换为字节数组,以便存储或传输的机制,以后,仍可以将字节数组转换回Java对象原有的状态。
实际上,序列化的思想是“冻结”对象状态,传输对象状态(写到磁盘、通过网络传输等等),然后“解冻”状态,重新获得可用的Java对象2[2]。
3.2.序列化必要性及目的
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。
这就需要有一种可以在两端传输数据的协议。
Java序列化机制就是为了解决这个问题而产生。
Java序列化支持的两种主要特性:
•Java的RMI使本来存在于其他机器的对象可以表现出就象本地机器上的行为。
•将消息发给远程对象时,需要通过对象序列化来传输参数和返回值.
Java序列化的目的:
•支持运行在不同虚拟机上不同版本类之间的双向通讯;
•定义允许JAVA类读取用相同类较老版本写入的数据流的机制;
•定义允许JAVA类写用相同类较老版本读取的数据流的机制;
•提供对持久性和RMI的序列化;
•产生压缩流且运行良好以使RMI能序列化;
•辨别写入的是否是本地流;
•保持非版本化类的低负载。
3.3.序列化算法实现
3.3.1.序列化算法解析
Serialization序列化是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程2[6]。
java中序列化算法的顺序:
1、子类的类信息描述
2、子类的字段信息描述(如果遇到类对象的属性,暂时用string的指针表示)3、父类的类信息描述
4、父类的字段信息描述
5、对父类的字段信息进行赋值
6、对子类的字段信息进行赋值
7、发现子类的字段为类对象时,描述该类对象,并查找该类对象的父类,以以上方式描述,然后赋值。
(1)对象序列化步骤
1.创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2.通过对象输出流的writeObject()方法写对象。
(2)对象反序列化步骤
1.创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2.通过对象输入流的readObject()方法读取对象。
3.3.2.序列化实现
(1) JDK类库中的序列化API
Java中用来实现序列化的类都在java.io包中,常用的类或接口有ObjectOutputStream类和ObjectInputStream类及Serialzable接口。
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Objectobj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
上面已经说过序列化分为两大部分:
序列化和反序列化,序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。
反序列化就是打开字节流并重构对象。
在序列化一个对象的时候,会先实例化一个ObjectOutputStream对象,然后调用其writeObject()方法;在反序列化(deserialize)的时候,则会实例化一个ObjectInputStream对象,然后调用其readObject()方法。
Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。
在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。
当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。
如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。
设置serialVersionUID默认的生成方式:
privatestaticfinallongserialVersionUID=545456546548431L;
serialVersionUID的作用:
serialVersionUID用来表明类的不同版本间的兼容性。
如果修改了此类,要修改此值。
否则以前用老版本的类序列化的类恢复时会出错。
在JDK中,可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID,对于Test.class,执行命令:
serialverTest。
为了在反序列化时,确保类版本的兼容性,最好在每个要序列化的类中加入privatestaticfinallongserialVersionUID这个属性,具体数值自己定义。
这样,即使某个类在与之对应的对象已经序列化出去后做了修改,该对象依然可以被正确反序列化。
否则,如果不显式定义该属性,这个属性值将由JVM根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败。
不显式定义这个属性值的另一个坏处是,不利于程序在不同的JVM之间的移植。
因为不同的编译器实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,出现因类版本不兼容而无法正确反序列化的现象出现。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。
Externalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式。
下面让我们来看一个对应的例子,类的内容如下:
importjava.io.*;
importjava.util.Date;
publicclassObjectSaver{
publicstaticvoidmain(String[]args)throwsException{
ObjectOutputStreamout=newObjectOutputStream
(newFileOutputStream("D:
""objectFile.obj"));
//序列化对象
Customercustomer=newCustomer("abc",20);
out.writeObject("你好!
");
out.writeObject(newDate());
out.writeObject(customer);
out.writeInt(123);//写入基本类型数据
out.close();
//反序列化对象
ObjectInputStreamin=newObjectInputStream
(newFileInputStream("D:
""objectFile.obj"));
System.out.println("obj1="+(String)in.readObject());
System.out.println("obj2="+(Date)in.readObject());
Customerobj3=(Customer)in.readObject();
System.out.println("obj3="+obj3);
intobj4=in.readInt();
System.out.println("obj4="+obj4);
in.close();
}
}
classCustomerimplementsSerializable{
privateStringname;
privateintage;
publicCustomer(Stringname,intage){
this.name=name;
this.age=age;
}
publicStringtoString(){
return"name="+name+",age="+age;
}
}
输出结果如下:
obj1=你好!
obj2=SatSep1522:
02:
21CST2007
obj3=name=abc,age=20
obj4=123
(2)实现Serializable接口
ObjectOutputStream只能对Serializable接口的类的对象进行序列化。
默认情况下,ObjectOutputStream按照默认方式序列化,这种序列化方式仅仅对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量,也不会序列化静态变量。
当ObjectOutputStream按照默认方式反序列化时,具有如下特点:
1)如果在内存中对象所属的类还没有被加载,那么会先加载并初始化这个类。
如果在classpath中不存在相应的类文件,那么会抛出ClassNotFoundException;
2)在反序列化时不会调用类的任何构造方法。
如果用户希望控制类的序列化方式,可以在可序列化类中提供以下形式的writeObject()和readObject()方法。
privatevoidwriteObject(java.io.ObjectOutputStreamout)throwsIOException
privatevoidreadObject(java.io.ObjectInputStreamin)throwsIOException,ClassNotFoundException;
当ObjectOutputStream对一个Customer对象进行序列化时,如果该对象具有writeObject()方法,那么就会执行这一方法,否则就按默认方式序列化。
在该对象的writeObjectt()方法中,可以先调用ObjectOutputStream的defaultWriteObject()方法,使得对象输出流先执行默认的序列化操作。
同理可得出反序列化的情况,不过这次是defaultReadObject()方法。
有些对象中包含一些敏感信息,这些信息不宜对外公开。
如果按照默认方式对它们序列化,那么它们的序列化数据在网络上传输时,可能会被不法份子窃取。
对于这类信息,可以对它们进行加密后再序列化,在反序列化时则需要解密,再恢复为原来的信息。
默认的序列化方式会序列化整个对象图,这需要递归遍历对象图。
如果对象图很复杂,递归遍历操作需要消耗很多的空间和时间,它的内部数据结构为双向列表。
在应用时,如果对某些成员变量都改为transient类型,将节省空间和时间,提高序列化的性能。
(3)实现Externalizable接口2.[3]
Externalizable接口继承自Serializable接口,如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。
Externalizable接口声明了两个方法:
publicvoidwriteExternal(ObjectOutputout)throwsIOException
publicvoidreadExternal(ObjectInputin)throwsIOException,ClassNotFoundException
前者负责序列化操作,后者负责反序列化操作。
在对实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法,这是有别于默认反序列方式的。
如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException:
novalidconstructor异常。
(4)序列化类的不同版本的序列化兼容性
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
privatestaticfinallongserialVersionUID;
以上serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。
如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。
为了提高哦啊serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。
显式地定义serialVersionUID有两种用途:
1)在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
2)在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
3.4.序列化异常
序列化对象期间可能抛出6种异常:
•InvalidClassException通常在重序列化流无法确定类型时或返回的类无法在取得对象的系统中表示时抛出此异常。
异常也在恢复的类不声明为public时或没有public缺省(无变元)构造器时抛出。
•NotSerializableException通常由具体化对象(负责自身的重序列化)探测到输入流错误时抛出。
错误通常由意外不变量值指示,或者表示要序列化的对象不可序列化。
•StreamCorruptedException在存放对象的头或控制数据无效时抛出。
•OptionalDataException流中应包含对象但实际只包含原型数据时抛出。
•ClassNotFoundException流的读取端找不到反序列化对象的类时抛出。
•IOException要读取或写入的对象发生与流有关的错误时抛出。
3.5.序列化范围
并不是所有的类都需要序列化,有的类是敏感的类,里面的数据不能公开。
而实现了序列化就很容易被破解,可以说没有秘密。
所以有隐私性不能保证。
序列化可以任意生成实例而不受限制,如果有的对象生成的实例是受限制的,比如只能生成10个实例,或者是单例的,这个很难保证。
所以不是所以的类都需要序列化,那么这就要提供一个接口或标识符,需要序列化的类要贴上标识符,这个标识符就是Serializable或者Externalizable,实现了任何一个接口就代表可以被序列化。
判断能否序列化的工具有很多,这里就不介绍了。
对类中的普通成员变量需要序列化,成员变量是对象的状态,如果没有成员变量则对象是不完整的。
静态变量和方法不需要序列化,静态变量是类属性,不属于某个具体实例,所以也不用保存,当恢复对象的时候直接取当前的静态变量即可;方法只是类的无状态指令,重建类的时候可以直接从类的信息中获取,所以也不需要被序列化。
3.6.transient保护敏感信息
上一节说明类中非静态的属性需要被序列化,但有些情况下,用
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 序列