概念
序列化:将数据结构或对象转换成二进制串的过程。
反序列化:反之。
常见的序列化协议
- Serializable Java的序列化方案
- Parcelable Android独有
- json,xml,protbuf … 广义的序列化
Serializable
public interface Serializable {
}
Serializable 用来标识当前类可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列
化。
public class Student implements Serializable {
//serialVersionUID唯一标识了一个可序列化的类,假设当前类已经序列化或者持久化到本地,然后修改对象的属性,比如去掉了某个属性,此时再去反序列化成该对象时,会报错,因为SerialVersionUID不一致了。
private static final long serialVersionUID = -2100492893943893602L;
private String name;
private String sax;
private Integer age;
//Course也需要实现Serializable接口 否则报错
private List<Course> courses;
//用transient关键字标记的成员变量不参与序列化(在被反序列化后,transient 变量的值被 设为初始值,如 int 型的是 0,对象型的是 null)
private transient Date createTime;
//静态成员变量属于类不属于对象,所以不会参与序列化(对象序列化保存的是对象的“状态”,也 就是它的成员变量,因此序列化不会关注静态变量)
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
Externalizable
package java.io;
//读写顺序要求一致、读写的成员变量的个数都一致
public interface Externalizable extends Serializable {
void writeExternal(ObjectOutput var1) throws IOException;
void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException;
}
序列化原理
单例
/**
* 单例类防止反序列化
*/
class Singleton implements Serializable{
public static Singleton INSTANCE = new Singleton();
private Singleton(){}
private Object readResolve(){
return INSTANCE;
}
}
/**
* 单例类如何防止反射
*/
class Singleton1 {
private static boolean flag = false;
private Singleton1(){
synchronized(Singleton.class){
if(flag == false){
flag = !flag;
} else {
throw new RuntimeException("单例模式被侵犯!");
}
}
}
private static class SingletonHolder {
private static final Singleton1 INSTANCE = new Singleton1();
}
public static Singleton1 getInstance(){
return SingletonHolder.INSTANCE;
}
}
或者直接用枚举
enum Num1{
ONE,TWO,THREE;
public void printValues(){
System.out.println("ONE: "+ ONE.ordinal() + ", TWO: " + TWO.ordinal() + ", THREE: " + THREE.ordinal());
}
}
/**
* Java的序列化机制针对枚举类型是特殊处理的。简单来讲,在序列化枚举类型时,只会存储枚举类的引用和枚举常量的名称。随后的反序列化的过程中,
* 这些信息被用来在运行时环境中查找存在的枚举类型对象。
*/
public class EnumSerializableTest {
//
public static void main(String[] args) throws Exception {
byte[] bs =SerializeableUtils.serialize(Num1.THREE);
Num1.THREE.printValues();
System.out.println("hashCode: " + Num1.THREE.hashCode());
System.out.println("反序列化后");
Num1 s1 = SerializeableUtils.deserialize(bs);
s1.printValues();
System.out.println("hashCode: " + s1.hashCode());
System.out.println("== " + (Num1.THREE == s1));
}
}
Q&A
序列化与持久化的关系和区别?
答: 序列化是为了进程间数据交互而设计的, 持久化是为了把数据存储下来而设计的。
序列化与反序列化后的对象是什么关系?
答:是一个深拷贝, 前后对象的引用地址不同,enmu例外。
Android 为什么要设计 bundle 而不是使用 HashMap 结构?
答:bundle 内部适用的是 ArrayMap, ArrayMap 相比 Hashmap 的优点是, 扩容方便, 每次扩容是原容量的一半, 在[百量] 级别, 通过二分法查找 key 和 value (ArrayMap 有两个数组, 一个存放 key 的 hashcode, 一个存放 key+value 的 Entry) 的效率要比 hashmap 快很多, 由于在内存中或者 Android 内部传输中一般数据量较小, 因此用 bundle 更为合适。
Android 中 intent/bundle 的通信原理以及大小限制?
答: Android 中的 bundle 实现了 parcelable 的序列化接口, 目的是为了在进程间进行通讯, 不同的进程共享一片固定大 小的内存, parcelable 利用 parcel 对象的 read/write 方法, 对需要传递的数据进行内存读写, 因此这一块共享内存不能 过大, 在利用 bundle 进行传输时, 会初始化一个 BINDER_VM_SIZE 的大小 = 1 * 1024 * 1024 - 4096 * 2, 即便通过 修改 Framework 的代码, bundle 内核的映射只有 4M, 最大只能扩展到 4M.
为何 Intent 不能直接在组件间传递对象而要通过序列化机制?
答: 因为 Activity 启动过程是需要与 AMS 交互, AMS 与 UI 进程是不同一个的, 因此进程间需要交互数据, 就必须序列化。具体如下:
Intent在启动其他组件时,会离开当前应用程序进程,进入ActivityManagerService进程intent.prepareToLeaveProcess()),这也就意味着,Intent所携带的数据要能够在不同进程间传输。首先我们知道,Android是基于Linux系统,不同进程之间的java对象是无法传输,所以我们此处要对对象进行序列化,从而实现对象在 应用程序进程和ActivityManagerService进程之间传输。
这里插入一张APP启动流程图,以及通信方式