1.Serializable接口
序列化与反序列化就是JAVA对象与一串字节流之间的相互转换, 我们在程序中创建的JAVA对象只存在于JVM中, 当程序退出时, 这些对象也就消失了, 而序列化正是为了将这些对象保存起来以仅将来使用,也可以将已经序列化的对象传送给其他JVM来使用,这些序列化的字节流是于JVM无关的, 也就是说一个JVM序列化的对象可以在另一个JVM中反序列化.
序列化分为两大部分:序列化和反序列化。 序列化是这个过程的第一部分,将数据分解成字节流,以便存储在文件中或在网络上传输。 反序列化就是打开字节流并重构对象。 对象序列化不仅要将基本数据类型转换成字节表示,有时还要恢复数据。 恢复数据要求有恢复数据的对象实例。 ObjectOutputStream中的序列化过程与字节流连接,包括对象类型和版本信息。 反序列化时,JVM用头信息生成对象实例,然后将对象字节流中的数据复制到对象数据成员中。
Java的对象序列化只要对象实现了Serializable接口
这个接口只是一个标记接口,不包含任何的方法,
使用JAVA提供的序列化机制有以下两条需要遵守的条件:
1.该类必须直接实现java.io.Serializable接口或者间接从其继承树中实现该接口(也就是他的某个父类实现了这个接口);
2.对于该类的所有无法序列化的属性(本文指字段field, 而不是严格意义上的属性property, 下同)必须使用transient修饰.
补充说明:
从其继承树中实现Serializable接口指的是该类的某个父类实现了这个接口, 要注意的是Object类并没有实现该接口, 也就是说默认的情况下我们定义的类是不支持序列化的, 而JDK提供的某些类如String, 数组等实现了该接口;
无法序列化的属性包括两种:一种是主观上不想保存的属性, 如动态生成的属性或者考虑到性能上的要求不准备保存的属性; 另一种是由于该属性的类型没有实现序列化而无法保存的属性, 如Thread类型的属性;
安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传输等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。
transient
变量修饰符,如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。
换句话来说就是,用transient关键字标记的成员变量不参与序列化过程。Serializable序列化不保存静态变量,可以使用Transient关键字对部分字段不进行序列化,也可以覆盖writeObject、readObject方法以实现序列化过程自定义。
场景:某个类的有些属性需要序列化,而其他属性不需要被序列化,打个比方,如果一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
序列化过程
如果我们想要序列化一个对象,首先要创建某些OutputStream(如FileOutputStream、ByteArrayOutputStream等),然后将这些OutputStream封装在一个ObjectOutputStream中。这时候,只需要调用writeObject()方法就可以将对象序列化,并将其发送给OutputStream(记住:对象的序列化是基于字节的,不能使用Reader和Writer等基于字符的层次结构)。而饭序列的过程(即将一个序列还原成为一个对象),需要将一个InputStream(如FileInputstream、ByteArrayInputStream等)封装在ObjectInputStream内,然后调用readObject()即可。
序列化算法一般会按步骤做如下事情:
◆将对象实例相关的类元数据输出。
◆递归地输出类的超类描述直到不再有超类。
◆类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。
◆从上至下递归输出实例的数据
对象序列化过程不仅仅保存单个对象,还能追踪对象内所包含的所有引用,并保存那些对象(这些对象也需实现了Serializable接口)
任何类型只要实现了Serializable接口,就可以被保存到文件中,或者作为数据流通过网络发送到别的地方。
反序列化
序列化机制并不要求该类具有一个无参的构造方法, 因为在反序列化的过程中实际上是去其继承树上找到一个没有实现Serializable接口的父类(最终会找到Object), 然后构造该类的对象, 再逐层往下的去设置各个可以反序列化的属性(也就是没有被transient修饰的非静态属性).
在反序列的JVM上必须能够找到该类(有可能序列化和反序列化并不是在同一个JVM上进行的), 否则就会抛出ClassNotFoundException;
由于ObjectInputStream.readObject()方法可以反序列化任何类的对象, 所以其返回类型为Object, 我们需要将其强转成具体的类;
如何对不满足序列化机制的两个要求的类进行序列化, 则会抛出NotSerializableException;
如果JVM发现序列化与反序列化的类文件"不相同", 则会抛出InvalidClassException.
Parcelable 实现过程
1.实现Parcelable接口
2.实现接口中的两个方法
public int describeContents();
public void writeToParcel(Parcel dest, int flags);
第一个方法是内容接口描述,默认返回0就可以了
第二个方法是将我们的对象序列化一个Parcel对象,也就是将我们的对象存入Parcel中 .
3.实例化静态内部对象CREATOR实现接口Parcelable.Creator,实例化CREATOR时要实现其中的两个方法,其中createFromParcel的功能就是从Parcel中读取我们的对象。
对比分析
Serializable是Java的实现方式,可能会频繁的IO操作,所以消耗比较大,但是实现方式简单 Parcelable是Android提供的方式,效率比较高,但是实现起来复杂一些Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据,而Serializable可将数据持久化方便保存,所以在需要保存或网络传输数据时选择Serializable.
在读写数据的时候,Parcelable是在内存中直接进行读写,而Serializable是通过使用IO流的形式将数据读写入在硬盘上.
Serializable在序列化操作的时候会产生大量的临时变量,(原因是使用了反射机制)从而可能导致GC的频繁调用。
Parcelable是以Ibinder作为信息载体的.在内存上的开销比较小,因此在内存之间进行数据传递的时候,使用Parcelable,