Java序列化

   Serialization(序列化)是一种将对象以一连串的字节描述的过程;反序列化deserialization是一种将这些字节重建成一个对象的过程。

Java序列化API提供一种处理对象序列化的标准机制。

Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生。


若我们的对象需要序列化,需要实现序列化接口Serializable接口,Serializable接口没有方法,更像是个标记。有了这个标记的Class就能被序列化机制处理。

序列化的作用:

1)该对象保存的数据需要一直使用,我们可以将其序列化后写入硬盘长久保存,下次程序启动后我们还可以将其读取回来继续使用。

2)将对象进行网络传输。

原理:

将Object转换为byte系列,就是系列化,反之是反序列化。

为了在byte流中存储对象。

使用writeObject(Object)/readObject()进行序列化和反序列化。


JavaBean规范,必须实现Serializable接口,JavaAPI中的类大多是JavaBean,基本都实现了Serializable。


1、ObjectOutputStream(序列化)和 ObjectInputStream(反序列化)

如果没有实现Serializable接口,就进行序列化就出现异常。

例子:


public class Demo2_Serializable {
    public static void main(String[] args) throws Exception {
    //serializable();
    noSerializable();
    }
    /** 反序列化*/
    public static void noSerializable() throws IOException, Exception{
        ObjectInputStream ois = null;
        try{
            FileInputStream fis = new FileInputStream("person.obj");
            /** 使用ObjectInputStream反序列化 */
            ois = new ObjectInputStream(fis);
            /** 读取字节并转换为对象,强制转换,如果有多个要按照系列化的顺序来顺序得到对象*/
            Persons person = (Persons)ois.readObject();
            Persons person1 = (Persons)ois.readObject();
            System.out.println(person);
            System.out.println(person1);
        } finally{
            if(ois!=null){
                ois.close();
            }
        }
    }
    /** 序列化 */
    public static void serializable() throws IOException{
        Persons person = new Persons("boss","123456",16,1);
        Persons person1 = new Persons("刘德华","123456",16,1);
        /**
        * 将对象保存在文件中
        * 1、创建用于写文件的字节输出流FileOutputStream
        * 2、创建用于序列化对象的ObjectOutputStream并让其处理FileOutputStream,这样就可以将对象序列化成字节不保存到文件。
        * 3、使用ObjectOutputStream将对象写出。
        */
        ObjectOutputStream oos = null;
        try{
            FileOutputStream fos = new FileOutputStream("person.obj");
            oos = new ObjectOutputStream(fos);
            //序列化之后写入文件
            oos.writeObject(person);
            oos.writeObject(person1);
            /**
            * 注:写出的只有属性值,但实际上写出的字节总量比所有属性值的总字节要大。
            */
        } finally{
            if(oos!=null){
                oos.flush();
                oos.close();
            }
        }
    }
}
class Persons implements Serializable{
    private String name;
    private String password;
    private int age;
    private int sex;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getSex() {
        return sex;
    }
    public void setSex(int sex) {
        this.sex = sex;
    }
    public Persons(String name, String password, int age, int sex) {
        super();
        this.name = name;
        this.password = password;
        this.age = age;
        this.sex = sex;
    }
    public Persons() {
        super();
    }
    @Override
    public String toString() {
    return "Persons [name=" + name + ", password=" + password + ", age="
    + age + ", sex=" + sex + "]";
    }
}


2、tarnsient关键字


tarnsient是java关键字,用来表示一个域不是该对象串行化的一部分,当一个对象被串行化的时候,tarnsient型变量的值不包括在串行化的表示中,然而非tarnsient型的变量是被包括进去。


反序列化后的对象,和原对象equals比较为true,但是他们不是同一个对象,且若属性也是引用类型,那么这些属性也不是同一个对象,但是equals比较也是true。


4、ByteArrayInputStream 和 ByteArrayOutStream


都有明确的来源和去向,两个流维护这一个字节数组。读写都在其内部维护的数组上了。


/**
* 深度复制对象
*/
public static Object deepClone(Object object){
    try {
        /**
        * 1、创建用于将字节写入字节数组的字节输入流ByteArrayOutputStream
        * 2、创建用于序列化对象的ObjectOutputStream并处理
        * 3、当对象被oos写出后,实际上就是通过ByteArrayOutputStream写到内部维护的字节数组上了,我们获取该数组
        * 4、创建一个用于从数组中读取数据的输入流ByteArrayInputStream,
        * 并将之前保存着的对象序列化后的字节的数组传递给这个流。
        * 5、创建用于反序列化对象的ObjectInputStream并处理ByteArrayInputStream
        * 6、使用OIS读取并反序列化对象达到深度复制的目录。
        */
        ByteArrayOutputStream baos = new ByteArrayOutputStream();//1
        ObjectOutputStream oos = new ObjectOutputStream(baos);//2
        oos.writeObject(object);//3
        //获取 维护的数组
        byte[] data = baos.toByteArray();
        //4 转化为输入流
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        //5
        ObjectInputStream ois = new ObjectInputStream(bais);
        //6
        Object copy = ois.readObject();
        oos.close();
        ois.close();
        return copy;   
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}
public static void main(String[] args) {
        Persons person = new Persons("吴","2332523",25,0);
        Persons person1 = new Persons("吴g","2332523",25,0);
        ArrayList<Persons> list = new ArrayList<Persons>();
        list.add(person);
        //list.add(person);   
        ArrayList<Persons> list2 = (ArrayList<Persons>)deepClone(list);
        //System.out.println(list.get(0)==list.get(1));
        System.out.println(list == list2);
        System.out.println(list.get(0) == list2.get(0));
        System.out.println(list.get(0).equals(list2.get(0)));
}



此外对于Java序列化的原理算法透析可参考http://developer.51cto.com/art/200908/147650.htm