《Java网络编程精解讲义9.ppt》由会员分享,可在线阅读,更多相关《Java网络编程精解讲义9.ppt(36页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、Java网络编程精解作者:孙卫琴作者:孙卫琴作者:孙卫琴作者:孙卫琴参考书籍:参考书籍:参考书籍:参考书籍:技术支持网址:技术支持网址:技术支持网址:技术支持网址:www.javathinker.orgwww.javathinker.org第9章 对象的序列化与反序列化参考Java网络编程精解的第9章n9.1 JDK类库中的序列化APIn9.2 实现Serializable接口n9.2.1 序列化对象图n9.2.2 控制序列化的行为n9.2.3 readResolve()方法在单例类中的运用n9.3 实现Externalizable接口n9.4 可序列化类的不同版本的序列化兼容性 第9章 对象
2、的序列化与反序列化n当两个Java进程进行远程通信时,一个进程能否把一个Java对象发送给另一个进程呢?答案是肯定的。不过,发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。n把Java对象转换为字节序列的过程称为对象的序列化;把字节序列恢复为Java对象的过程称为对象的反序列化。第9章 对象的序列化与反序列化n当程序运行时,程序所创建的各种对象都位于内存中,当程序运行结束,这些对象就结束生命周期。n如图9-1所示,对象的序列化主要有两种用途:n(1)把对象的字节序列永久的保存到硬盘上,通常存放在一个文件中。n(2)在网络上传送对象的字节
3、序列。第9章 对象的序列化与反序列化9.1 JDK类库中的序列化APInjava.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。njava.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化成一个对象,并将其返回。9.1 JDK类库中的序列化APIn只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则ObjectOutputStre
4、am的writeObject(Object obj)方法会抛出IOException。n实现Serializable或Externalizable接口的类也称为可序列化类。nExternalizable接口继承自Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式。nJDK类库中的部分类(如String类、包装类和Date类等)都实现了Serializable接口。9.1 JDK类库中的序列化API9.1 JDK类库中的序列化APIn对象的序列化主要包括以下步骤。n(1)创建一个对象
5、输出流,它可以包装一个其他类型的目标输出流,比如文件输出流:ObjectOutputStream out=new ObjectOutputStream(new fileOutputStream(D:objectFile.obj);n(2)通过对象输出流的writeObject()方法写对象:out.writeObject(hello);/写一个String对象out.writeObject(new Date();/写一个Date对象9.1 JDK类库中的序列化APIn对象的反序列化主要包括以下步骤。n(1)创建一个对象输入流,它可以包装一个其他类型的源输入流,比如文件输入流:ObjectInp
6、utStream out=new ObjectInputStream(new FileInputStream(D:objectFile.obj);n(2)通过对象输入流的readObject()方法读取对象:String obj1=(String)out.readObject();/读取一个String对象Date obj2=(Date)out.readObject();/读取一个Date对象n为了能读出正确的数据,必须保证向对象输出流写对象的顺序与从对象输入流读对象的顺序一致。9.1 JDK类库中的序列化APIn例程9-1的ObjectSaver类的main()方法先向objectFile.
7、obj文件写入三个对象和一个int类型的数据,然后再依次把它们从文件中读入到内存中。1.public static void main(String agrs)throws Exception 2.ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(D:objectFile.obj);3.String obj1=hello;4.Date obj2=new Date();5.Customer obj3=new Customer(Tom,20);6./序列化对象7.out.writeObject(obj1);8.ou
8、t.writeObject(obj2);9.out.writeObject(obj3);10.out.writeInt(123);/写入基本类型的数据11.out.close();9.1 JDK类库中的序列化API12./反序列化对象 13.ObjectInputStream in=new ObjectInputStream(new FileInputStream(D:objectFile.obj);14.String obj11=(String)in.readObject();15.System.out.println(obj11:+obj11);16.System.out.println(
9、obj11=obj1:+(obj11=obj1);17.Date obj22=(Date)in.readObject();18.System.out.println(obj22:+obj22);19.System.out.println(obj22=obj2:+(obj22=obj2);20.Customer obj33=(Customer)in.readObject();21.System.out.println(obj33:+obj33);22.System.out.println(obj33=obj3:+(obj33=obj3);23.24.int var=in.readInt();/读
10、取基本类型的数据25.System.out.println(var:+var);26.27.in.close();28.9.1 JDK类库中的序列化APIn例程9-2的SimpleServer服务器从命令行读取用户指定的类名,创建该类的一个对象,然后向客户端两次发送这个对象。n例程9-3的SimpleClient客户程序负责接收服务器发送的对象。n按照默认方式序列化时,如果由一个ObjectOutputStream对象多次序列化同一个对象,那么由一个ObjectInputStream对象反序列化出来的也是同一个对象。9.2 实现Serializable接口nObjectOuputStream只
11、能对实现了Serializable接口的类的对象进行序列化。n默认情况下,ObjectOuputStream按照默认方式序列化,这种序列化方式仅仅对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量,也不会序列化静态变量。9.2 实现Serializable接口n例程9-4的Customer1类中,定义了一些静态变量、非transient的实例变量,以及transient的实例变量。public class Customer1 implements Serializable private static int count;/用于计算Customer
12、对象的数目 private static final int MAX_COUNT=1000;private String name;private transient String password;.9.2 实现Serializable接口n当ObjectInputStream按照默认方式反序列化时,有以下特点:n如果在内存中对象所属的类还没有被加载,那么会先加载并初始化这个类。如果在classpath中不存在相应的类文件,那么会抛出ClassNotFoundException。n在反序列化时不会调用类的任何构造方法。n如果一个实例变量被transient修饰符修饰,那么默认的序列化方式不会
13、对它序列化。根据这一特点,可以用transient修饰符来修饰以下类型的实例变量:n(1)实例变量不代表对象的固有的内部数据,仅仅代表具有一定逻辑含义的临时数据。n(2)实例变量表示一些比较敏感的信息(比如银行账户的口令),出于安全方面的原因,不希望对其序列化。n(3)实例变量需要按照用户自定义的方式序列化,比如经过加密后再序列化。在这种情况下,可以把实例变量定义为transient类型,然后在writeObject()方法中对其序列化。9.2.1 序列化对象图n类与类之间可能存在关联关系。以下代码创建的三个对象之间的关联关系。/客户Tom有两个订单,订单编号分别为“number1”和“num
14、ber2”Customer2 customer=new Customer2(Tom);Order2 order1=new Order2(number1,customer);Order2 order2=new Order2(number2,customer);customer.addOrder(order1);customer.addOrder(order2);9.2.1 序列化对象图9.2.1 序列化对象图n当通过ObjectOutputStream对象的writeObject(customer)方法序列化Customer2对象时,会不会序列化与它关联的Order2对象呢?答案是肯定的。n在默
15、认方式下,对象输出流会对整个对象图进行序列化。当程序执行writeObject(customer)方法时,该方法不仅序列化Customer2对象,还会把两个与它关联的Order2对象也进行序列化。n当通过ObjectInputStream对象的readObject()方法反序列化Customer2对象,实际上会对整个对象图反序列化。9.2.1 序列化对象图n按照默认方式序列化对象A时,实际上被序列化的对象图中包括:对象A、对象B、对象C、对象D、对象E、对象F和对象G。9.2.2 控制序列化的行为n如果用户希望控制类的序列化方式,可以在可序列化类中提供以下形式的writeObject()方法和
16、readObject()方法:private void writeObject(java.io.ObjectOutputStream out)throws IOExceptionprivate void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException;9.2.2 控制序列化的行为n当ObjectOutputStream对一个Customer对象进行序列化时,如果该Customer对象具有writeObject()方法,那么就会执行这一方法,否则就按默认方式序列化。n在Custom
17、er对象的writeObject()方法中,可以先调用ObjectOutputStream的defaultWriteObject()方法,使得对象输出流先执行默认的序列化操作。9.2.2 控制序列化的行为n当ObjectInputStream对一个Customer对象进行反序列化时,如果该Customer对象具有readObject()方法,那么就会执行这一方法,否则就按默认方式反序列化。n在Customer对象的readObject()方法中,可以先调用ObjectInputStream的defaultReadObject()方法,使得对象输入流先执行默认的反序列化操作。9.2.2 控制序列
18、化的行为n在以下情况,可以考虑采用用户自定义的序列化方式,从而控制序列化的行为:n(1)确保序列化的安全性,对敏感的信息加密后再序列化,在反序列化时则需要解密。n(2)确保对象的成员变量符合正确的约束条件。n(3)优化序列化的性能。n(4)便于更好的封装类的内部数据结构,确保类的接口不会被类的内部实现所束缚。9.2.3 readResolve()方法在单例类中的运用n单例类是指仅有一个实例的类。在系统中具有惟一性的组件可作为单例类,这种类的实例通常会占用较多的内存,或者实例的初始化过程比较冗长,因此随意创建这些类的实例会影响系统的性能。n如果一个类提供了readResolve()方法,那么在执
19、行反序列化操作时,先按照默认方式或者用户自定义的方式进行反序列化,最后再调用readResolve()方法,该方法返回的对象为反序列化的最终结果。9.3 实现Externalizable接口nExternalizable接口继承自Serializable接口。如果一个类实现了Externalizable接口,那么将完全由这个类控制自身的序列化行为。Externalizable接口中声明了两个方法:npublic void writeExternal(ObjectOutput out)throws IOException npublic void readExternal(ObjectInput
20、 in)throws IOException,ClassNotFoundException9.3 实现Externalizable接口nwriteExternal()方法负责序列化操作,readExternal()方法负责反序列化操作。在对实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造方法,这是有别与默认反序列化方式的。n由此可见,一个类如果实现了Externalizable接口,那么它必须具有public类型的不带参数的构造方法,否则这个类无法反序列化。9.4 可序列化类的不同版本的序列化兼容性n假定Customer5类有两个版本1.0和2.0,如
21、果要把基于1.0的序列化数据反序列化为2.0的Customer5对象,或者把基于2.0的序列化数据反序列化为1.0的Customer5对象,会出现什么情况呢?如果可以成功的反序列化,则意味着不同版本之间对序列化兼容,反之,则意味着不同版本之间对序列化不兼容。n凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态常量:private static final long serialVersionUID;n以上serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码做了修改,再重新编译,新生成的类文件的serialVersionU
22、ID的取值有可能也会发生变化。9.4 可序列化类的不同版本的序列化兼容性n类的serialVersionUID的默认值依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。n为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显式的定义serialVersionUID,为它赋予明确的值。n显式的定义serialVersionUID有两种用途:n(1)在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID。n(2)在某些场合,不希
23、望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。练习题1n问题:以下哪些类实现了问题:以下哪些类实现了java.io.Serializable接口接口?n选项选项:na)java.io.OutputStream类类nb)java.lang.String类类nc)java.lang.Integer类类nd)java.util.Date类类n答案答案:b,c,d练习题2n问题:以下哪一段代码向问题:以下哪一段代码向C:data.obj文件中写入一个文件中写入一个Data对象?对象?n选项选项:na)ObjectOutputStream out=new
24、 ObjectOutputStream(new fileOutputStream(C:data.obj);out.writeDate(new Date();nb)ObjectOutputStream out=new ObjectOutputStream(new fileOutputStream(C:data.obj);out.writeObject(new Date();nc)FileOutputStream out=new FileOutputStream(C:data.obj);out.writeObject(new Date();nd)FileOutputStream out=new F
25、ileOutputStream(new ObjectOutputStreamC:data.obj);out.writeObject(new Date();n答案答案:b练习题3n问题:问题:默认的序列化方式有什么特点?默认的序列化方式有什么特点?n选项选项:na)仅仅对对象的非仅仅对对象的非transient的实例变量进行序列的实例变量进行序列化。化。nb)不会序列化对象的不会序列化对象的transient的实例变量。的实例变量。nc)会序列化静态变量。会序列化静态变量。nd)会序列化对象的所有会序列化对象的所有public类型的成员变量。类型的成员变量。n答案答案:a,b练习题4n问题:问题
26、:默认的反序列化方式有什么特点?默认的反序列化方式有什么特点?n选项选项:na)可能会调用类的静态代码块。可能会调用类的静态代码块。nb)一定会调用类的静态代码块。一定会调用类的静态代码块。nc)不会调用类的任何构造方法。不会调用类的任何构造方法。nd)会调用类的不带参数的构造方法。会调用类的不带参数的构造方法。n答案答案:a,c练习题5n问题:问题:对于一个类的两个不同版本,如果它们对于一个类的两个不同版本,如果它们的序列化版本号的序列化版本号serialVersionUIDserialVersionUID相同,就一相同,就一定序列化兼容,这句话是否正确?定序列化兼容,这句话是否正确?n选项
27、选项:na)正确正确 nb)不正确不正确n答案答案:b练习题6n问题:问题:关于关于ExternalizableExternalizable接口,下面哪些说法正确?接口,下面哪些说法正确?n选项选项:na)Externalizable接口继承自接口继承自Serializable接口。接口。nb)writeExternal()方法负责序列化操作。方法负责序列化操作。nc)readExternal()方法负责反序列化操作。方法负责反序列化操作。nd)在对实现了在对实现了Externalizable接口的类的对象进行反序列化接口的类的对象进行反序列化时,一定会先调用类的不带参数的构造方法。时,一定会
28、先调用类的不带参数的构造方法。ne)在对实现了在对实现了Externalizable接口的类的对象进行反序列化接口的类的对象进行反序列化时,一定会先调用类的静态代码块。时,一定会先调用类的静态代码块。nf)一个类如果实现了一个类如果实现了Externalizable接口,那么它必须具有接口,那么它必须具有public类型的不带参数的构造方法。类型的不带参数的构造方法。n答案答案:a,b,c,d,f练习题7n问题:问题:可序列化类中的可序列化类中的writeObject()writeObject()方法和方法和readObjectreadObject()()方法使用什么访问控制修饰符?方法使用什么访问控制修饰符?n选项选项:na)publicnb)没有没有nc)protectednd)privaten答案答案:d