序列化与反序列化

来源:互联网 发布:linux升级rpm版本号 编辑:程序博客网 时间:2024/06/06 04:50

定义

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

应用场景

(1)实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)

(2)利用序列化实现远程通信,即在网络上传送对象的字节序列。

实现方式

所有实现序列化的类都必须实现Serializable 接口,它是一种标记接口,里面没有任何方法。当序列化的时候,需要用到ObjectOutputStream 里面的writeObject();当反序列化的时候,需要用到ObjectInputStream 里面的readObject()方法。

序列化图示:


反序列化图示:


特点

(1)当一个对象被序列化时,只序列化对象的非静态成员变量,不能序列化任何成员方法和静态成员变量。

(2)当一个父类实现序列化时,子类自动实现序列化,不需要显示实现Serializable 接口。

(3)当只有子类实现Serializable接口,父类没有时,反序列化时生成子类时,只能调用父类的无参构造函数作为默认的父对象。因此当我们取父对象的变量值时,它的值是调用父类无参构造函数后的值。如果你考虑到这种序列化的情况,在父类无参构造函数中对变量进行初始化,否则的话,父类变量值都是默认声明的值,如int型的默认是0string型的默认是 null

(4)当一个对象的实例变量引用了其他对象时,序列化该对象时,也把引用对象进行序列化。

(5)对象中被static 或者transient 修饰的变量,在序列化时其变量值是不被保存的。

(6)建议显示的定义serialVersionUID,默认为1L,另一种是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段 。


显示定义serialVersionUID的原因

(1)类的不同版本对序列化是否兼容要求

  • 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID
  • 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。 

(2)当你序列化了一个类实例后,希望更改一个字段或添加一个字段,不设置serialVersionUID,所做的任何更改都将导致无法反序化旧有实例,并在反序列化时抛出一个异常java.io.InvalidClassException。如果你添加了serialVersionUID,在反序列旧有实例时,新添加或更改的字段值将设为初始化值(对象为null,基本类型为相应的初始默认值),字段被删除,将不设置。

(3)提高程序的运行效率。如果在类中没有显示声明serialVersionUID,那么在序列化的时候会通过计算得到该值。如果显示声明该值的话,会省去计算的过程。


序列化算法

序列化算法一般会按步骤做如下事情:

◆ 将对象实例相关的类元数据输出。

◆ 递归地输出类的超类描述直到不再有超类。

◆ 类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。

◆ 从上至下递归输出实例的数据

这一块可以参考我转载的一篇博客:http://blog.csdn.net/qq_35181209/article/details/77359985


提问

一个问题:ArrayList 和LinkedList 能否序列化?

都可以序列化。ArrayList 里面的数组elementData 是声明为transient 的,表示ArrayList在序列化的时候,默认不会序列化这些数组元素。

原因:ArrayList 实际上是动态数组,每次在放满以后会扩容,如果数组扩容后,实际上只放了一个元素,那就会序列化很多null 元素,浪费空间,所以ArrayList 把元素数组设置为transient,仅仅序列化其他符合要求的实例数据。


对象自定义序列化动作

对象实现Serializable 接口以后,序列化的动作不仅取决于对象本身,还取决于执行序列化的对象。以ObjectOutputStream 为例,如果ArrayList 或自定义对象实现了writeObject(),readObject(),那么在序列化和反序列化的时候,就按照自己定义的方法来执行动作,所以ArrayList 就自定义了writeObject 和readObject 方法,然后在writeObject 方法内完成数组元素的自定义序列化动作,在readObject 方法内完成数组元素的自定义反序列化动作。

请看ArrayList自定义序列化反序列化源码:

    private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException{        // Write out element count, and any hidden stuff        int expectedModCount = modCount;        s.defaultWriteObject();        // Write out size as capacity for behavioural compatibility with clone()        s.writeInt(size);        // Write out all elements in the proper order.        for (int i=0; i<size; i++) {            s.writeObject(elementData[i]);        }        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        }    }
private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        elementData = EMPTY_ELEMENTDATA;        // Read in size, and any hidden stuff        s.defaultReadObject();        // Read in capacity        s.readInt(); // ignored        if (size > 0) {            // be like clone(), allocate array based upon size not capacity            ensureCapacityInternal(size);            Object[] a = elementData;            // Read in all elements in the proper order.            for (int i=0; i<size; i++) {                a[i] = s.readObject();            }        }    }








原创粉丝点击