版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第7章对象序列化
7.1对象序列化7.2序列化操作7.3定制序列化
7.1对 象 序 列 化
7.1.1序列化的概念
计算机软件的主要工作是处理数据。在处理数据时,经常会遇到需要处理一组相关的数据,例如,要保存这样一条聊天信息“张三:李四:你好”,这条信息中包括三个内容,即发信者、接收者和聊天内容,通常需要使用三个变量进行存储。在C语言中使用结构体实现储存一组相关的数据,如例7-1所示。
【例7-1】
使用结构体存储数据。
StructMessage{
charsender[10];
charreceiver[10];
charcontent[30];
}msg[20];
【例7-2】利用结构体存储一组数据,并保存在文件中或者从文件中读取。
Filefp=File();
//存储的方法
for(i=0;i<20;i++)
fwrite(&msg[i],sizeof(StructMessage),1,fp);
//读取的方法
for(i=0;i<20;i++)
fread(&msg[i],sizeof(StructMessage),1,fp);
Java语言中,取消了结构体,除了基本数据类型都是对象。如果要存储一组相关的数据,就必须采用自定义相关类。
【例7-3】使用Java定义存储消息的类。对比两种语言对聊天消息的描述方法,其主要区别在于,使用Java语言描述方式不仅包含成员变量,而且包括了成员方法,从而实现了信息的封装。
在Java语言中,处理数据均采用流的方式,其最大的特点就是数据的输入和输出都按顺序进行。Java语言是面向对象的语言,除了字符串对象可以通过writeUTF()和readUTF()直接操作外,其他对象不能直接存储和通过网络发送。当需要对使用例7-3的类所定义的实例对象进行存储或者发送时,就应该将该对象进行序列化操作,使其转变为流形式,然后才能实现存储和通过网络发送。序列化是一种用来处理对象流的机制,对象流是将对象的内容进行流化,然后将经过流化后的对象进行读/写操作和用于网络传输。发送端将对象序列化为流,接收端反序列化从数据流重新构造对象,保证了对象的完整性和可传递性。即,序列化的过程是将对象写入字节流,反序列化的过程是从字节流中读取对象。将对象状态转换成字节流之后,可以用java.io包中的各种字节流类将其保存到文件中,或者通过管道在线程之间传输,或者通过网络连接将对象数据发送到另一主机。
序列化在网络程序设计(如Socket、RMI(RemoteMethodInvocation)、JMS(JavaMessageService)、EJB(EnterpriseJavaBean))中有广泛的应用。7.1.2序列化的实现
对象序列化是将携带信息的对象转换为可以用于存储或传输形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区,然后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。序列化分为两大部分:序列化和反序列化,即对象序列化不仅要将对象转换成字节表示,有时还要恢复对象。
序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。反序列化就是打开字节流,按照被恢复数据的对象实例重构对象。这两个过程结合起来,就可以轻松地存储和传输对象数据。序列化的目的:
● 以某种存储形式使自定义对象持久化;
● 将对象从一个地方传递到另一个地方;
● 使程序更具维护性。
Java序列化是基于TCP协议的,实现比较简单,通常不需要编写保存和恢复对象状态的定制代码。但是要求被序列化的类是实现了java.io.Serializable接口的,这样就可以转换成字节流或从字节流恢复,不需要在类中增加额外任何代码。Serializable接口定义如图7-1所示。图7-1Serializable接口定义
在Serinalizable接口中未提供任何方法,即不存在需要被覆盖的方法。通过implements实现Serializable接口的目的在于标识该对象是可被序列化的。
在Java语言中并不是任何个类都可被序列化。通常将用于存储信息的类进行序列化,因为这些类所定义的对象是用于存储和传输的,例如学生信息类、消息信息类。而用于执行指令和操作的类是不用于序列化的,例如将学生信息储存和读取类、消息的发送和接收类。尤其要注意,涉及线程的类和与特定JVM有非常复杂关系的类,都不能进行序列化操作。序列化使其他代码可以查看或修改那些不序列化便无法访问的对象实例数据。确切地说,代码执行序列化需要特殊的权限,即指定了SerializationFormatter标志的Security Permission。在默认策略下,通过Internet下载的代码或Intranet代码不会授予该权限;只有本地计算机上的代码才被授予该权限。
通常,实现Serializable接口的类所声明的对象实例中所有字段都会被序列化,这意味着数据会被表示为实例的序列化数据。这样能够解释该格式的代码有可能能够确定这些数据的值,而不依赖于该成员的可访问性。类似地,反序列化从序列化的表示形式中提取数据,并直接设置对象状态,这也与可访问性规则无关。7.1.3ObjectInputStream与ObjectOutputStream
为了将存储于对象中的数据保存在磁盘文件或通过网络发送等,需要使用基于对象的输入/输出流。java.io包提供两个类用于序列化对象传输,分别是对象输入类ObjectInput Stream与对象输出流类ObjectOutputStream。
ObjectInputStream用于从底层输入流中读取对象类型的数据,ObjectOutputStream用于将对象类型的数据写入到底层输入流。ObjectInputStream与ObjectOutputStream类所读/写的对象必须实现了Serializable接口。对象中被transient和static修饰的成员变量不会被读取和写入。● ObjectInputStream从字节流重构对象,实现反序列化过程。反序列化时,JVM用头信息生成对象实例,然后将对象字节流中的数据复制到对象数据成员中。
● ObjectOutputStream负责将对象写入字节流,实现序列化过程,该序列化过程与字节流连接,包括对象类型和版本信息。
用一个输出流(如FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream对象的writeObject(Objectobj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。当需要将数据存储在本地磁盘时,在Java中需要使用ObjectOutputStream类,该类扩展DataOutput接口。writeObject()方法是最重要的方法,用于对象序列化。如果对象包含其他对象的引用,则writeObject()方法递归序列化这些对象。由于writeObject()可以序列化整组交叉引用的对象,因此,同一ObjectOutputStream实例可能不小心被请求序列化同一对象。每个ObjectOutputStream维护序列化的对象引用表,防止发送同一对象的多个拷贝。
【例7-4】
在文件tmp中保存两个对象,其中一个是字符串“Today”,另一个是当前的日期。//
序列化
today's
date
到一个文件中.
FileOutputStream
f
=
new
FileOutputStream("tmp");
ObjectOutputStream
s
=
new
ObjectOutputStream(f);
s.writeObject("Today");
s.writeObject(new
Date());
s.flush();
当需要将数据从本地磁盘读取出时,在Java中需要使用ObjectInputStream类,该类扩展DataInput接口。readObject()为重要的方法,实现从字节流中反序列化对象。每次调用readObject()方法都返回流中下一个Object。对象字节流并不传输类的字节码,而是包括类名及其签名。readObject()收到对象时,JVM装入头中指定的类。如果找不到这个类,则readObject()抛出ClassNotFoundException。如果需要传输对象数据和字节码,则可以用RMI框架。ObjectInputStream的其余方法用于定制反序列化过程。
【例7-5】从文件tmp中读取数据对象。
//从文件中反序列化
string
对象和
date
对象
FileInputStream
in
=
new
FileInputStream(“tmp”);
ObjectInp
utStream
s
=
new
ObjectInputStream(in);
String
today
=
(String)s.readObject();
Date
date
=
(Date)s.readObject();
7.2序 列 化 操 作
7.2.1序列化存储
通常,实现序列化接口的类是用于存储和传输的类,用于操作的类是不进行序列化的。首先,通过代码学习在Java中如何通过序列化实现聊天信息的储存。在下面的例子中定义一个聊天信息类,该类用于存储信息,需要实现Serializable接口。并且该类需要在存储和读取时都使用,通常将自定义信息类、存储类和读取类设计在一个包中,否则会出现无法找到类的异常发生。代码注释如下:
①第1行通常为了保证能够顺利地进行序列化和反序列化操作,声明为Serializable接口的存储类和实现操作类应在一个message包中,以保证访问的路径;
②第3行定义的存储类实现了Serializable接口;
③第4~6行声明了三个成员变量;
④第7~11行声明了构造方法;
⑤第12~16行定义了输出方法show()。
例7-6中定义的信息类需要实现Serializable接口,所以需要引用io类库。在类中的成员变量有三个,分别记录消息的发送者、接收者和内容。成员方法有两个,一个是用于赋初值的构造方法Message(),另一个是用于输出显示的方法show()。接下来,定义将该类进行存储操作的类,即进行序列化操作的类。该类中首先申明了两个需要被存储的信息对象,然后申明文件输出对象,通过ObjectOutputStream.write
Object()方法,将信息对象保存到文件中。
【例7-7】
定义用于存储学生信息的类,执行序列化操作,保存数据至文件。
代码注释如下:
①第1行与实现序列化的存储类在一个message包中;
②第9~10行使用Message类声明了两个被存储对象;
③第12~13行声明对象输出流;
④第14~15行通过writeOjbect()输出对象,实现序列化操作。
运行该程序,将在当前目录下生成一个新的文件msgDB.dat,用于存储信息。使用记事本软件打开该文件,可以看到存储的内容,如图7-2所示。图7-2msgDB.dat文件内容
【例7-8】
定义用于存储信息的类,执行反序列化操作,从文件中读取数据还原成对象。
代码注释如下:
①第1行与实现序列化的存储类在一个message包中;
②第9~10行声明对象输入流;
③第12~13行采用readObject()实现反序列化操作,因为读取时默认类型为Object,所以需要进行一次强制类型转换为需要的类对象;
④第16~17行输出内容。
程序运行结果如图7-3所示。图7-3反序列化结果7.2.2序列化传输
序列化操作不仅可以在本地磁盘保存类数据,也可以将对象用于网络传输。首先,定义一个用于存储消息的消息类,用于记录消息发送者、消息接收者、消息内容,在这里直接利用了例7-6,注意Message类的包信息需要进行相应修改。另外分别编写客户端和服务器端程序。
【例7-9】实现客户端,用于序列化对象,发送对象流。代码注释如下:
①第9行通过TCP连接指定的服务器localhost的8080端口;
②第10行套接字上获得输出流,使其作为对象输出流的参数;
③第11行准备要传输的对象;
④第12~13行通过writeObject()发送信息对象。
【例7-10】
实现服务器端,用于接收对象流,进行反序列化操作。代码注释如下:
①第10行启动本地的TCP8080端口,监听客户端的连接请求;
②第12行与客户端实现连接;
③第13行获得套接字的getInputStream(),使其作为ObjectInputStream的参数;
④第15行获得对象,并进行反序列化操作,还原为Message对象;
⑤第16~17行输出结果。
服务器端接收序列化及反序列化操作运行结果如图7-4所示。图7-4服务器端运行结果从运行结果可见,服务器收到的信息对象直接输出是不可读的,除非该信息类中覆盖了Object中的toString()方法。
7.3定 制 序 列 化
7.3.1序列化成员变量
在Java中类的成员变量可以是简单数据类型,也可以是复杂数据类型,如字符串类类型、自定义类类型等。当某个类实现了序列化接口时,其未加transient和static修饰的成员变量一同被序列化。所以,当使用类类型定义成员变量时,要保证这些类是可以被序列化的,即实现了Serializable接口。如String类的定义中就声明实现了Serializable接口,如图7-5所示。图7-5String类的定义当实现序列化的自定义类的成员变量中还包含了其他的自定义类对象时,就需要该成员变量实现序列化接口。
代码注释如下:
①第6行使用Detail类对象作为成员变量;
②第20~27行实现Detail类的定义,此时它未实现Serializable接口。
可以从以上代码可以看出,新定义了Details类,用于存储消息内容和发送时间,但是该类未实现序列化接口。在Message类中,采用Details类定义了一个details对象。在对此类进行编译时,未出现任何错误提示。
在运行对象流发送端时将会抛出异常,提示Details类未实现Serializable接口,如图7-6所示。可见一个实现序列化接口中的成员变量如果是类对象,那么该类也必须实现了序列化接口,否则在运行时就会出现无法实现序列化操作的异常提示信息。图7-6异常提示信息7.3.2定制序列化
对于一个正在运行的类对象来说,存在以下概念:
● 生命周期:从JVM提供一个对象所需要的资源,到释放该对象资源为止,就是一个对象的生命周期。
● 短暂存储:对象在内存中构建后,会随着程序的运行和结束而改变和结束,这就是短暂存储。在Java语言中,序列化过程中,使用transient关键字表示属性或对象是短暂的。
● 永久存储:对象被保存在永久设备中,这些永久设备包括文件、磁盘、数据库等,该对象数据不会随程序结束而消失。对象序列化是对象永久化的一种机制。序列化通常可以自动完成,但有时可能要对这个过程进行控制。对于任何可能包含重要的安全性数据的对象,应该使该对象不可序列化。如果它必须为可序列化的,也需要指定特定字段来保存不可序列化的重要数据。如果无法实现这一点,则应注意该数据会被公开给任何拥有序列化权限的代码,并确保不让任何恶意代码获得该权限。
Java在对类实现serializable接口时,可通过关键字static或transient为类中的数据成员变量进行定制,以实现保护特殊数据的目的。
将数据成员声明为transient后,序列化过程就无法将其加进对象字节流中,也就没有从transient数据成员发送的数据。后面数据反序列化时,要重建数据成员(因为它是类定义的一部分),但不包含任何数据,因为这个数据成员不向流中写入任何数据。
在本例的基础上运行例
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 学校2024-2025学年度德育工作计划
- 进行性肢端黑变病的临床护理
- 【培训课件】销售技能培训 顾问式实战销售
- 产后胳膊疼的健康宣教
- 低磷血症的临床护理
- 《教学管理》课件
- 变形杆菌性角膜炎的临床护理
- JJF(陕) 077-2021 水泥胶砂试体成型振实台校准规范
- 幼儿教师培训课件:《信息交流》
- 创新教学方法提升幼儿园教育质量计划
- 300KW并网电站方案
- 护理伦理学 绪论 高校版
- 防坠落装置技术规范书
- 民主推荐测评满意率测评表(样表)及汇总表
- 粮食熏蒸安全操作规程资料
- 最新自来水公司抄表员先进事迹材料-范文精品
- 汽轮机本体结构简介
- (完整版)小学教师安全工作考核细则
- 三轴水泥土搅拌桩
- 幼儿园健康教案:系鞋带
- 拖欠农民工工资排查表
评论
0/150
提交评论