Java序列化和反序列化探索

基本概念

序列化就是把一个Java对象变为字节序列(byte[]数组)的过程。
反序列化其实就是把一个字节序列变回Java对象的过程。

对象序列化的两种用途:

  • 1 将Java对象变为字节序列永久的保存在硬盘上的文件里
  • 2 将Java对象变为字节序列方便在网络上传输

举例:

  • 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
  • 当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

JDK类库中的序列化API

  • java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

  • java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

  • 只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。

  • 对象序列化包括如下步骤:
      1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
      2) 通过对象输出流的writeObject()方法写对象。

  • 对象反序列化的步骤如下:
      1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
      2) 通过对象输入流的readObject()方法读取对象。

serialVersionUID的作用

  • 字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量

  • 为什么要使用:

    当我们要在序列化的类中添加一个属性时,假如一开始此类中没有显示的声明版本号ID,那么在执行反序列化操作时就会报异常,原因就是修改过后的class和以前的类不兼容了,出于安全机制考虑,程序抛出了异常,没有反序列化成功。其中主要原因就是一开始没有指定类的serialVersionUID,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用。

  • 显式地定义serialVersionUID有两种用途:
       1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
       2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。

要传递的类实现Parcelable接口传递对象(android专用)

  • 概念
    进行Android开发的时候,无法将对象的引用传给Activities或者Fragments,我们需要将这些对象放到一个Intent或者Bundle里面,然后再传递
    不过不同于将对象进行序列化,Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了。
  • Parcelable作用:
    1)永久性保存对象,保存对象的字节序列到本地文件中;
    2)通过序列化对象在网络中传递对象;
    3)通过序列化在进程间传递对象。
  • 实现
    • 1 describeContents方法。内容接口描述,默认返回0就可以

    • 2 writeToParcel 方法。打包数据到Parcel容器保存

    • 3 静态的Parcelable.Creator接口,本接口有两个方法:

    createFromParcel(Parcel in)  
    //从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑    层。
    
    newArray(int size) 
    //创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。
    
  • Serializable 和Parcelable的对比
    android上应该尽量采用Parcelable,效率至上
    (1)编码上:
    Serializable代码量少,写起来方便;Parcelable代码多一些
    (2)效率上:
    Parcelable的速度比 Serializable高十倍以上;serializable的迷人之处在于你只需要对某个类实现Serializable 接口即可。Serializable 接口是一种标识接口(marker interface),这意味着无需实现方法,Java便会对这个对象进行高效的序列化操作。
    这种方法的缺点是使用了反射,序列化的过程较慢。这种机制会在序列化的时候创建许多的临时对象,容易触发垃圾回收。
    Parcelable方式的实现原理是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的数据类型,这样也就实现传递对象的功能了

你可能感兴趣的:(Java序列化和反序列化探索)