JAVA对象的序列化与反序列化(详细).ppt
对象的序列化与反序列化,主要内容,概述,当两个进程进行远程通信时,彼此可以发送各种类型的数据,包括文本图片音频视频等,都会以二进制序列的形式在网络上传送.当两个java进程进行通信时,一个进程能否把一个java对象发送给另一个进程呢?,答案是肯定的!,概述,如何才能做到呢1)发送方需要把这个java对象转换为字节序列,才能在网上传送2)接收方需要把字节序列再恢复为java对象.把java对象转换为字节序列的过程称为对象的序列化,.把字节序列恢复为java对象的过程称为对象的反序列化.,序列化与反序列化,对象序列化两种用途,把对象的字节序列永久的保存到硬盘上,通常存放在一个文件中.在网络上传送对象的字节序列.,主机1,主机2,文件,网络传输,存储到文件,9.1.jdk类库中的序列化API,如何才能做到呢1)发送方需要把这个java对象转换为字节序列,才能在网上传送2)接收方需要把字节序列再恢复为java对象.把java对象转换为字节序列的过程称为对象的序列化,.把字节序列恢复为java对象的过程称为对象的反序列化.,序列化与反序列化,9.1jdk类库中的序列化API,java.io.ObjectOutputStream:代表对象输出流它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。代表对象输入流,它的readObject()方法从一个源输入流中读取字节,再把它们反序列化成一个对象,并将其返回。,9.1jdk类库中的序列化API,哪些类可以被序列化呢?只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则ObjectOutputStream的writeObject(Object obj)方法会抛出IOException。实现了Serializable或Externalizable接口的类也称为可序列化类。Externalizable接口继承Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为。而仅实现Serializable接口的类可以采用默认的序列化方式。Jdk的部分类 如StringDate等都实现了Serializable接口,9.1jdk类库中的序列化API,假定一个Customer类,它的对象需要序列化。可以有以下三种方式进行如果customer类仅仅实现了Serializable接口的类,那么会按照以下方式进行序列化和反序列化:ObjectOutputStream采用默认的序列化方式,对Customer对象的非transient的实例变量进行序列化。ObjectInputStream采用默认的反序列化方式,对customer对象的非transient的实例变量进行反序列化。,9.1jdk类库中的序列化API,2.如果customer类仅仅实现了Serializable接口,并且还定义了readObject(ObjectInputStream in)和writeObject(ObjectOutputStream out),那么会按照以下方式进行序列化和反序列化:ObjectOutputStream会调用Customer对象的writeObject(ObjectOutputStream out)方法进行序列化。ObjectInputStream会调用Customer对象的readObject(ObjectInputStream in)方法进行反序列化。,9.1jdk类库中的序列化API,3.如果customer类实现了Externalizable接口,那么Customer类必须实现readExternal(ObjectInput in)和writeExternal(ObjectOutput out)方法,那么会按照以下方式进行序列化和反序列化:ObjectOutputStream会调用Customer对象的writeExternal(ObjectOutput out)方法进行序列化。ObjectInputStream会调用Customer对象的readExternal(ObjectInput in)方法进行反序列化。,9.1jdk类库中的序列化API类框图,9.1jdk类库中的序列化步骤,创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流:ObjectOutputStream out=new ObjectOutputStream(new fileOutputStream(“D:objectfile.obj”);,9.1jdk类库中的序列化步骤,通过对象输出流的writeObject()方法写对象,如:Out.writeObject(“hello”);Out.writeObject(new Date();,9.1jdk类库中的反序列化步骤,创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流:ObjectInputStream in=new ObjectInputStream(new fileIutputStream(“D:objectfile.obj”);,9.1jdk类库中的反序列化步骤,通过对象输入流的readObject()方法读取对象,如:String obj1=(String)in.readObject();Date obj2=(Date)in.readObject();为了能正确读取数据,必须保证向对象输出流写对象的顺序与从对象输入流读对象的顺序一致,例9.1jdk类库中序列化程序,import java.io.*;import java.util.*;public class ObjectSaver public static void main(String agrs)throws Exception ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream(D:objectFile.obj);String obj1=hello;Date obj2=new Date();Customer obj3=new Customer(Tom,20);/序列化对象 out.writeObject(obj1);out.writeObject(obj2);out.writeObject(obj3);out.writeInt(123);out.close();,例9.1jdk类库中序列化程序,/反序列化对象 ObjectInputStream in=new ObjectInputStream(new FileInputStream(D:objectFile.obj);String obj11=(String)in.readObject();System.out.println(obj11:+obj11);System.out.println(obj11=obj1:+(obj11=obj1);Date obj22=(Date)in.readObject();System.out.println(obj22:+obj22);System.out.println(obj22=obj2:+(obj22=obj2);Customer obj33=(Customer)in.readObject();System.out.println(obj33:+obj33);System.out.println(obj33=obj3:+(obj33=obj3);int var=in.readInt();System.out.println(var:+var);in.close();,例9.1jdk类库中序列化程序,class Customer implements Serializable private String name;private int age;public Customer(String name,int age)this.name=name;this.age=age;public String toString()return name=+name+,age=+age;,例程9-2 SimpleServer,该服务器从命令行读取用户指定的类名,创建该类的一个对象,然后向客户端两次发送这个对象。结果表明:按照默认方式序列化时,如果有一个ObjectOutputStream对象多次序列化同一个对象,那么有一个ObjectInputStream对象反序列化出来的对象也是同一个对象。,例程9-2 SimpleServer,import java.io.*;import.*;import java.util.*;public class SimpleServer public void send(Object object)throws IOException ServerSocket serverSocket=new ServerSocket(8000);while(true)Socket socket=serverSocket.accept();OutputStream out=socket.getOutputStream();ObjectOutputStream oos=new ObjectOutputStream(out);oos.writeObject(object);oos.writeObject(object);oos.close();socket.close();,例程9-2 SimpleServer,public static void main(String args)throws IOException Object object=null;if(args.length0,例程9-2 SimpleServer,else if(args.length0,例程9-3 SimpleClient,import java.io.*;import.*;import java.util.*;public class SimpleClient public void receive()throws Exception Socket socket=new Socket(localhost,8000);InputStream in=socket.getInputStream();ObjectInputStream ois=new ObjectInputStream(in);Object object1=ois.readObject();Object object2=ois.readObject();System.out.println(object1);System.out.println(object2);System.out.println(object1与object2是否为同一个对象:+(object1=object2);public static void main(String args)throws Exception new SimpleClient().receive();,9.2实现Serializable接口,ObjectOutputStream只能对实现了Serializable接口的类的对象进行序列化,默认情况下,ObjectOutputStream按照默认方式进行序列化,这种方式只能对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量和静态变量,例如例程9-4,9.2实现Serializable接口,public class Customer1 implements Serializable private static int count;/用于计算Customer对象的数目 private static final int MAX_COUNT=1000;private String name;private transient String password;static System.out.println(调用Customer1类的静态代码块);public Customer1()System.out.println(调用Customer1类的不带参数的构造方法);count+;public Customer1(String name,String password)System.out.println(调用Customer1类的带参数的构造方法);this.name=name;this.password=password;count+;public String toString()return count=+count+MAX_COUNT=+MAX_COUNT+name=+name+password=+password;,9.2实现Serializable接口,先运行”java SimpleServer Customer1”,再运行命令“java SimpleClient”,服务器端打印的结果如下:调用Customer1类的静态代码块调用Customer1类的带参数的构造方法待发送的对象信息:count=1 MAX_COUNT=1000 name=Tom password=1234客户端打印的信息(反序列化时不会调用类的构造方法)调用Customer1类的静态代码块count=0 MAX_COUNT=1000 name=Tom password=nullcount=0 MAX_COUNT=1000 name=Tom password=nullObject1与Object2是否为同一个对象:true,9.2实现Serializable接口,先运行”java SimpleServer Customer1”,再运行命令“java SimpleClient”,服务器端打印的结果如下:调用Customer1类的静态代码块调用Customer1类的带参数的构造方法待发送的对象信息:count=1 MAX_COUNT=1000 name=Tom password=1234客户端打印的信息(反序列化时不会调用类的构造方法)调用Customer1类的静态代码块count=0 MAX_COUNT=1000 name=Tom password=nullcount=0 MAX_COUNT=1000 name=Tom password=nullObject1与Object2是否为同一个对象:true,9.3序列化对象图,类与类之间可能存在关联关系。如例程9-5,customer2与order2之间存在一对多的双向关联关系,9.3序列化对象图,public class Customer2 implements Serializable private String name;private Set orders=new HashSet();static(调用Customer2类的静态代码块);public Customer2()(调用Customer2类的不带参数的构造方法);public Customer2(String name)(调用Customer2类的带参数的构造方法);this.name=name;public void addOrder(Order2 order)orders.add(order);public String toString()String result=super.toString()+rn+orders+rn;return result;,9.3序列化对象图,class Order2 implements Serializable private String number;private Customer2 customer;public Order2()(调用Order2类的不带参数的构造方法);public Order2(String number,Customer2 customer)(调用Order2类的带参数的构造方法);this.number=number;this.customer=customer;,9.3序列化对象图,在例程9-2中以下代码建立了他们的关联关系:else if(args.length0,Customer2对象,Order2对象,Order2对象,9.3序列化对象图,序列化customer2对象时是否会序列化与它关联的对象呢?序列化对象时,会序列化对象customer2以及所有从customer2直接或间接导航到的对象。,Customer2对象,Order2对象,Order2对象,9。2。2控制序列化的行为,如果用户希望控制序列化行为,可以在序列化类中提供以下方法:private void writeObject(ObjectOutputStream ou)在该方法中可以调用ObjectOutputStream拥有的defaultWriteObject(ObjectOutputStream ou),使得对象输出流先执行默认的序列化操作private void readObject(ObjectInputStream in)在该方法中同样可以调用ObjectInputStream的defaultReadObject()方法。,9。2。2控制序列化的行为,以下情况下,可以考虑用户自定义的序列化方式,从而控制序列化行为:确保序列化安全性,对敏感信息加密后再序列化,反序列化时需解密。确保对象的成员变量符合正确的约束条件优化序列化的性能。便于更好的封装类的内部数据结构,确保类的接口不会被类的内部实现所束缚,9.2.2控制序列化的行为,import java.io.*;public class Customer3 implements Serializable private static int count;/用于计算Customer3对象的数目 private static final int MAX_COUNT=1000;private String name;private transient String password;static(调用Customer3类的静态代码块);public Customer3()(调用Customer3类的不带参数的构造方法);count+;public Customer3(String name,String password)(调用Customer3类的带参数的构造方法);this.name=name;this.password=password;count+;,序列化的安全性,9。2。2控制序列化的行为,/*加密数组,将buff数组中的每个字节的每一位取反*例如13的二进制为00001101,取反后为11110010*/private byte change(byte buff)for(int i=0;ij,9.2.2控制序列化的行为,private void writeObject(ObjectOutputStream stream)throws IOException stream.defaultWriteObject();/先按默认方式序列化 stream.writeObject(change(password.getBytes();stream.writeInt(count);private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException stream.defaultReadObject();/先按默认方式反序列化 byte buff=(byte)stream.readObject();password=new String(change(buff);count=stream.readInt();,9.2.2控制序列化的行为9-7,public class Customer implements Serializable private int age;public Customer(int age)if(age0)/合法性检查 throw new IllegalArgumentException(年龄不能小于零);this.age=age;public String toString()return age=+age;private void readObject(ObjectInputStream stream)throws IOException,ClassNotFoundException stream.defaultReadObject();/先按默认方式反序列化 if(age0)/合法性检查 throw new IllegalArgumentException(年龄不能小于零);,确保对象的成员变量符合正确的约束条件,9.2.2控制序列化的行为,public static void main(String args)throws Exception Customer customer=new Customer(25);System.out.println(Before Serialization:+customer);ByteArrayOutputStream buf=new ByteArrayOutputStream();/把Customer对象序列化到一个字节缓存中 ObjectOutputStream o=new ObjectOutputStream(buf);o.writeObject(customer);byte byteArray=buf.toByteArray();for(int i=0;ibyteArray.length;i+)System.out.print(byteArrayi+);if(i%10=0,确保对象的成员变量符合正确的约束条件,9.2.2控制序列化的行为,/篡改序列化数据 byteArraybyteArray.length-4=-1;byteArraybyteArray.length-3=-1;byteArraybyteArray.length-2=-1;byteArraybyteArray.length-1=-10;/从字节缓存中反序列化Customer对象 ObjectInputStream in=new ObjectInputStream(new ByteArrayInputStream(byteArray);customer=(Customer)in.readObject();/-10 System.out.println(After Serialization:+customer);,确保对象的成员变量符合正确的约束条件,