java-序列化、反序列化

52 阅读5分钟

定义以及相关概念

  1. 由于在系统底层,数据的传输形式是简单的字节序列形式传递,即在底层,系统不认识对象,只认 识字节序列,而为了达到进程通讯的目的,需要先将数据序列化,而序列化就是将对象转化字节序 列的过程。相反地,当字节序列被运到相应的进程的时候,进程为了识别这些数据,就要将其反序 列化,即把字节序列转化为对象
  2. 无论是在进程间通信、本地数据存储又或者是网络数据传输都离不开序列化的支持。而针对不同场 景选择合适的序列化方案对于应用的性能有着极大的影响。
  3. 从广义上讲,数据序列化就是将数据结构或者是对象转换成我们可以存储或者传输的数据格式的一 个过程,在序列化的过程中,数据结构或者对象将其状态信息写入到临时或者持久性的存储区中, 而在对应的反序列化过程中,则可以说是生成的数据被还原成数据结构或对象的过程。
  4. 这样来说,数据序列化相当于是将我们原先的对象序列化概念做出了扩展,在对象序列化和反序列 化中,我们熟知的有两种方法,其一是Java语言中提供的Serializable接口,其二是Android提供的 Parcelable接口。而在这里,因为我们对这个概念做出了扩展,因此也需要考虑几种专门针对数据 结构进行序列化的方法,如现在那些个开放API一般返回的数据都是JSON格式的,又或者是我们 Android原生的SQLite数据库来实现数据的本地存储,从广义上来说,这些都可以算做是数据的序列化

序列化

将数据结构或对象转换成二进制串的过程。

反序列化

将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

序列化/反序列化的目的

简单的概括

  • 序列化: 主要用于网络传输,数据持久化,一般序列化也称为编码(Encode)
  • 反序列化: 主要用于从网络,磁盘上读取字节数组还原成原始对象,一般反序列化也称为解码

Android程序员该如何选择序列化方案

Serializable 有以下几个特点:

  • 可序列化类中,未实现 Serializable 的属性状态无法被序列化/反序列化
  • 也就是说,反序列化一个类的过程中,它的非可序列化的属性将会调用无参构造函数重新创建
  • 因此这个属性的无参构造函数必须可以访问,否者运行时会报错
  • 一个实现序列化的类,它的子类也是可序列化的

Parcelable与Serializable如何选择

  • 在使用内存方面,Parcelable比Serializable性能高,所以推荐使用Parcelable。
  • Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
  • Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性,在外界有变化的情况下,建议使用Serializable

使用示图

image.png

几个面试相关的问题

- Android里面为什么要设计出Bundle而不是直接用Map结构
Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数 据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删 除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况 下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候, 认准一手 微信:meetjava HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据 量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用 ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保 证更快的速度和更少的内存占用。 另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可 序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。 而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为 了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。
- Android中Intent/Bundle的通信原理及大小限制
Intent 中的 Bundle 是使用 Binder 机制进行数据传送的。能使用的 Binder 的缓冲区是有大小限 制的(有些手机是 2 M),而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区 就更小了( 有人以前做过测试,大约一个线程可以占用 128 KB)。所以当你看到 The Binder transaction failed because it was too large 这类 TransactionTooLargeException 异常时,你应 该知道怎么解决了
- 为何Intent不能直接在组件间传递对象而要通过序列化机制?
Intent在启动其他组件时,会离开当前应用程序进程,进入ActivityManagerService进程 (intent.prepareToLeaveProcess()),这也就意味着,Intent所携带的数据要能够在不同进程间 传输。首先我们知道,Android是基于Linux系统,不同进程之间的java对象是无法传输,所以我 们此处要对对象进行序列化,从而实现对象在 应用程序进程 和 ActivityManagerService进程 之间 传输。 而Parcel或者Serializable都可以将对象序列化,其中,Serializable使用方便,但性能不如Parcel 容器,后者也是Android系统专门推出的用于进程间通信等的接口