Java序列化与反序列化

        当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

把Java对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为Java对象的过程称为对象的反序列化。

对象的序列化主要有两种用途:
1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2) 在网络上传送对象的字节序列。

一. JDK类库中的序列化API
         java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。、
        只有实现了Serializable接口的类的对象才能被序列化。
对象序列化包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象。
 

import java.io.*;
import java.util.Date;
/**
 * 对象的序列化和反序列化测试类.    
 */
public class ObjectSaver {
       public static void main(String[] args) throws Exception {
              ObjectOutputStream out = new ObjectOutputStream
                     (new FileOutputStream("D:""objectFile.obj"));
             //序列化对象
             Customer customer = new Customer("阿蜜果", 24);
             out.writeObject("你好!");
             out.writeObject(new Date());
             out.writeObject(customer);
             out.writeInt(123); //写入基本类型数据
             out.close();
             //反序列化对象
             ObjectInputStream in = new ObjectInputStream
             (new FileInputStream("D:""objectFile.obj"));
              System.out.println("obj1=" + (String) in.readObject());
              System.out.println("obj2=" + (Date) in.readObject());
              Customer obj3 = (Customer) in.readObject();
              System.out.println("obj3=" + obj3);
              int obj4 = in.readInt();
              System.out.println("obj4=" + obj4);
              in.close();
       }
}

class Customer implements Serializable {
       private String name;
       private transient String password;
       private int age;
       public Customer(String name, String password, int age) {
              this.name = name;
              this.password = password;
              this.age = age;
       }
       public String toString() {
              return "name=" + name + ",password=" + password + ", age=" + age;
       }
}

 输出结果如下:

obj1=你好!
obj2=Sat Sep 15 22:02:21 CST 2007
obj3=name=阿蜜果,password=null,age=24
obj4=123

 从上面可以看到transient的变量password没有被序列化,这就在类中可以灵活地设置不想要被序列化的成员变量,注:方法不能用transient修饰。

二.实现Serializable接口
ObjectOutputStream只能对Serializable接口的类的对象进行序列化。默认情况下,ObjectOutputStream按照默认方式序列化,这种序列化方式仅仅对对象的非transient的实例变量进行序列化,而不会序列化对象的transient的实例变量。还有类的static类型的变量也不会被序列化,static类型的变量会留存在JVM的栈中,读取的时候也是读取栈中的变量值,而不是从序列化对象中读取。

当ObjectOutputStream按照默认方式反序列化时,具有如下特点:
1)  如果在内存中对象所属的类还没有被加载,那么会先加载并初始化这个类。如果在classpath中不存在相应的类文件,那么会抛出ClassNotFoundException;
2)  在反序列化时不会调用类的任何构造方法。


        有些对象中包含一些敏感信息,这些信息不宜对外公开。如果按照默认方式对它们序列化,那么它们的序列化数据在网络上传输时,可能会被不法份子窃取。对于这类信息,可以对它们进行加密后再序列化,在反序列化时则需要解密,再恢复为原来的信息。

        默认的序列化方式会序列化整个对象图,这需要递归遍历对象图。如果对象图很复杂,递归遍历操作需要消耗很多的空间和时间,它的内部数据结构为双向列表。
        在应用时,如果对某些成员变量都改为transient类型,将节省空间和时间,提高序列化的性能。

你可能感兴趣的:(java,jdk,数据结构,网络应用)