深拷贝与Serializable简介

来源:互联网 发布:守望先锋mac能玩吗 编辑:程序博客网 时间:2024/06/11 02:49

上次我们说了Copy Constructor与clone()方法。谈到用拷贝构造函数和clone()方法来实现深拷贝会很麻烦。
那么有没有什么简单的方法来实现深拷贝呢?答案是有的,使用Java Serializable来实现深拷贝。

0. 数据类模型

什么是Serializable?我们来看下代码。

public interface Serializable {}

和之前的Cloneable是一个样子,与其说是一个接口,更像是一个属性。
我们建立一个嵌套深度为3的数据类。
注:这不是一个很好的面向对象的封装方式。此处只为缩减代码量。正式编程中请不要随便暴露类变量。

public class DataClass implements Serializable{    A a;    double valueC;    public DataClass(){        a = new A();        valueC = 1.05;    }    public DataClass(int valueA, float valueB, double valueC){      this.valueC = valueC;      a = new A(valueA, valueB);    }    @Override    public String toString(){        StringBuilder sb = new StringBuilder();        sb.append("a = ").append(a.valueA).append("\n");        sb.append("b = ").append(a.b.valueB).append("\n");        sb.append("c = ").append(valueC).append("\n");        return sb.toString();    }}

其中,A类声明如下。

public class A implements Serializable {    int valueA;    B b;    public A(){        valueA = 100;    }    public A(int valueA, float valueB){        this.valueA = valueA;        b = new B(valueB);    }    @Override    public String toString(){        StringBuilder sb = new StringBuilder();        sb.append("a = ").append(valueA).append("\n");        sb.append("b = ").append(b.valueB).append("\n");        return sb.toString();    }}

B类声明如下。

public class B implements Serializable {    float valueB;    public B(){        valueB = 10f;    }    public B(float valueB){        this.valueB = valueB;    }}

1. 深拷贝的实现

核心思想是用ObjectOutputStream来实现类的序列化;用ObjectInputStream来实现类的反序列化。
承载输出数据,以及接受的输入数据可以是:来自内存(ByteArrayOutputStream),来自文件(FileOutputStream),来自网络(Socket.getOutputStream())等。
因为深拷贝,我们写到内存里就可以,不需要对硬盘进行访问。

我们对2个DataClass和1个A类进行深拷贝。
实现代码如下。

    DataClass serial1 = new DataClass(1,1.1f,1.2);    DataClass serial2 = new DataClass(2,2.1f,2.2);    A serial3 = new A(3,3.3f);    //Serialize    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();    ObjectOutputStream objectOut = new ObjectOutputStream(byteOut);    objectOut.writeObject(serial1);    objectOut.writeObject(serial2);    objectOut.writeObject(serial3);    objectOut.close();    byte[] buffer = byteOut.toByteArray();    //Deserialize    ByteArrayInputStream byteIn = new ByteArrayInputStream(buffer);    ObjectInputStream objectIn = new ObjectInputStream(byteIn);    DataClass deSerial1 = (DataClass) objectIn.readObject();    DataClass deSerial2 = (DataClass) objectIn.readObject();    A deSerial3 = (A) objectIn.readObject();    System.out.println("Serialization:");    System.out.println("serial1:");    System.out.println(serial1);    System.out.println("serial2:");    System.out.println(serial2);    System.out.println("serial3:");    System.out.println(serial3);    System.out.println("Deserialization:");    System.out.println("de-serial1");    System.out.println(deSerial1);    System.out.println("de-serial2");    System.out.println(deSerial2);    System.out.println("de-serial3");    System.out.println(deSerial3);

得到结果如下:

Serialization:serial1:a = 1b = 1.1c = 1.2serial2:a = 2b = 2.1c = 2.2serial3:a = 3b = 3.3Deserialization:de-serial1a = 1b = 1.1c = 1.2de-serial2a = 2b = 2.1c = 2.2de-serial3a = 3b = 3.3

进一步验证是否是深拷贝,我们进行如下修改。

    serial1.a.b.valueB = 99.9f;    System.out.println("serial1:");    System.out.println(serial1);    System.out.println("de-serial1");    System.out.println(deSerial1);

得到结果如下。

serial1:a = 1b = 99.9c = 1.2de-serial1a = 1b = 1.1c = 1.2

可知,确实为深拷贝。

如果一个类的某些变量不想被序列化传递,可以声明为transient. 例如:

transient A a;

这样a这个变量就不会被序列化传递。
与此同时,带来的问题是,在反序列化的时候,对于一个类会抛出空指针异常(NullPointerException). 对于8大基础数据类型,则会被初始化为初始值(0, false等)

2. Serializable简介

你以为这就是Serializable的作用?不,这只是其冰山一角的功能罢了。Serializable最大的作用是可以将数据序列化后通过网络传输。相当于Java内建的XML, json.
以DataClass(1, 1.1f, 1.2)为例,其序列化格式如下(从左到右,从上到下):

DataClass 8-byte version number h0 2 A double A(1, 1.1f) 1.2 h1

其中h0和h1是句柄。

Serializable的其他用途我们下次再聊。