1.什么是序列化和反序列化
数据序列化就是将数据结构或者是对象转换成我们可以存储或者传输的数据格式的一个过程,在序列化的过程中,数据结构或者对象将其状态信息写入到临时或者持久性的存储区中,而在对应的反序列化过程中,则是生成的数据被还原成数据结构或对象的过程。一句话概括序列化就是将数据结构或对象转换成二进制串的过程
,反序列化就是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
json的拼装和解析也可以看作一种序列化和反序列化
2.为什么需要序列化
由于在系统底层,数据的传输形式是简单的字节序列(二进制)形式传递,即在底层,系统不认识对象,只认识字节序列,而为了达到进程通讯的目的,需要先将数据序列化,而序列化就是将对象转化字节序列的过程。相反地,当字节序列被运到相应的进程的时候,进程为了识别这些数据,就要将其反序列化,即把字节序列转化为对象
3.序列化的作用
为数据传递提供基础
4.序列化的是实现方式
a.Serializable接口
是 Java 提供的序列化接口
public interface Serializable { }
b.Externalizable
是实现了Serializable的一个子接口
public interface Externalizable extends Serializable {
void writeExternal(ObjectOutput var1) throws IOException;
void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException;
}
和Serializable的区别就是通过writeExternal方法自定义需要序列化的变量,哪些是我们需要的,我们就自己把它序列化进去,不像Serializable,他是默认将所有可以序列化的变量都序列化了,可定制性不强;同理,通过readExternal读取我们需要的参数。如下:
虽然类Man中定义了三个变量,但是我们指定了序列化只会对age和name进行,所以打印的情况会是下边这样,因为没有指定des的序列化,所以des打印结果是null
class Man implements Externalizable{
public int age;
public String name;
public String des;
public Man (){}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(age);
out.writeObject(name);
System.out.println("writeExternal");
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = in.readInt();
name = (String) in.readObject();
System.out.println("readExternal");
}
}
public static void main(String[] args) {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(CurPath+"/ren.txt"));
Man obj = new Man ();
obj.age = 1;
obj.des = "我是我";
obj.name = "名字的";
oos.writeObject(obj);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(CurPath+"/ren.txt"));
Man m = (Man ) ois.readObject();
System.out.println(m.age);
System.out.println(m.name);
System.out.println(m.des);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
打印结果:
1
名字的
null
c.Parcellable
Parcelable是Android提供的序列化的接口,Parcelable相对于Serializable的使用相对复杂一些,但Parcelable的效率相对Serializable高很多,号称快10倍,
Parcelable是Android SDK提供的,它是基于内存的,由于内存读写速度高于硬盘,因此Android中的跨进程对象的传递一般使用Parcelable
Parcelable与Serializable的性能比较
1.Serializable性能分析
Serializable是Java中的序列化接口,其使用起来简单但开销较大(因为Serializable在序列化过程中使用了反射机制,故而会产生大量的临时变量,从而导致频繁的GC),并且在读写数据过程中,它是通过IO流的形式将数据写入到硬盘或者传输到网络上。
2.Parcelable性能分析
Parcelable则是以IBinder作为信息载体,在内存上开销比较小,因此在内存之间进行数据传递时,推荐使用Parcelable,而Parcelable对数据进行持久化或者网络传输时操作复杂,一般这个时候推荐使用Serializable。
虽然Parcelable的性能要强于Serializable,但是仍然有特殊的情况需要使用Serializable,而不去使用Parcelable,因为Parcelable无法将数据进行持久化,因此在将数据保存在磁盘的时候,仍然需要使用后者,因为前者无法很好的将数据进行持久化.(原因是在不同的Android版本当中,Parcelable可能会不同,因此数据的持久化方面仍然是使用Serializable)
相关面试题
1.Android里面为什么要设计出Bundle而不是直接用Map结构
1.Bundle内部是由ArrayMap实现的,ArrayMap的内部实现是两个数组,一个int数组是存储对象数据对应下标,一个对象数组保存key和value,内部使用二分法对key进行排序,所以在添加、删除、查找数据的时候,都会使用二分法查找,只适合于小数据量操作,如果在数据量比较大的情况下,那么它的性能将退化。而HashMap内部则是数组+链表结构,所以在数据量较少的时候,HashMap的Entry Array比ArrayMap占用更多的内存。因为使用Bundle的场景大多数为小数据量,我没见过在两个Activity之间传递10个以上数据的场景,所以相比之下,在这种情况下使用ArrayMap保存数据,在操作速度和内存占用上都具有优势,因此使用Bundle来传递数据,可以保证更快的速度和更少的内存占用。
2.另外一个原因,则是在Android中如果使用Intent来携带数据的话,需要数据是基本类型或者是可序列化类型,HashMap使用Serializable进行序列化,而Bundle则是使用Parcelable进行序列化。而在Android平台中,更推荐使用Parcelable实现序列化,虽然写法复杂,但是开销更小,所以为了更加快速的进行数据的序列化和反序列化,系统封装了Bundle类,方便我们进行数据的传输。
2.Android中Intent/Bundle的通信原理及大小限制
Intent 中的 Bundle 是使用 Binder 机制进行数据传送的。能使用的 Binder 的缓冲区是有大小限制的(有些手机是 2 M),而一个进程默认有 16 个 Binder 线程,所以一个线程能占用的缓冲区就更小了( 有人以前做过测试,大约一个线程可以占用 128 KB)。所以当你看到 The Bindertransaction failed because it was too large 这类 TransactionTooLargeException 异常时,你应该知道怎么解决了
3.为何Intent不能直接在组件间传递对象而要通过序列化机制?
Intent在启动其他组件时,会离开当前应用程序进程,进入ActivityManagerService进程(intent.prepareToLeaveProcess()),这也就意味着,Intent所携带的数据要能够在不同进程间传输。首先我们知道,Android是基于Linux系统,不同进程之间的java对象是无法传输,所以我们此处要对对象进行序列化,从而实现对象在 应用程序进程 和 ActivityManagerService进程 之间
传输。而Parcel或者Serializable都可以将对象序列化,其中,Serializable使用方便,但性能不如Parcel容器,后者也是Android系统专门推出的用于进程间通信等的接口