序列化
想要在intent上传输对象的时候,直接传输是做不到的,需要对这个对象处理一下,而这个处理的过程,就叫做序列化。
关于序列化的定义,百度百科这样定义:
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。
比如说内存中有一组的对象集合,我们想要把这里的数据持久的保存下来。
而序列化,就是将对象保存成一连串字节描述的过程;相反,反序列化,就是将这一连串的字节描述恢复成对象的过程。
我是学Android的嘛,序列化在Android中可以用在把对象在网络上传输、intent意图传递等。
目前准备学习的序列化有两个,分别是Java的Serializable接口,还有Android特有的Parcelable接口,今天先着手一个较为简单的serializable接口。
Serializable:表示将一个对象转化成可存储或者可传输的状态,序列化之后的对象可以在网络上进行传输,也可以存储在本地。
Parcelable:将一个完整的对象进行分解,而分解后的每一个部分都是Intent所能支持的数据类型,因此实现传递对象的功能。
关于Serializable和Parcelable的两种方式,如何选择?
关于两者,先来看看它两个之间的区别:
1. Serializable是通过使用IO流的形式将数据读写入在硬盘上;而Parcelable则是在内存中直接进行读写。
2. Serializable在序列化的过程中会产生大量的临时文件,所以会产生频繁GC;Parcelable是以IBinder为载体,在内存中开销较小。
3. 因为Serializable是在磁盘中,所以它是数据持久化的;而Parcelable是在内存中,所以它不是。
从上面来看,Parcelable的效率要比Serializable高一些,一般情况下都是使用Parcelable,那么什么时候使用Serializable呢?
Parcelable不能使用在要将数据存储在磁盘上的情况,因为在外界有变化的情况下,Parcelable不能很好的保证数据的持续性。尽管Serializable效率低点,但此时还是建议使用Serializable 。
Serializable的用法
Serializable是Java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化。
我们先将对象实现这个空接口,紧接这生成它所对应的 serialVersionUID 。
public class Girl implements Serializable {
private static final long serialVersionUID = -7684134237751913941L;
private String name;
private int birth;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getBirth() {
return birth;
}
public void setBirth(int birth) {
this.birth = birth;
}
}
serialVersionUID 一共有两种实现,一种是默认的。
private static final long serialVersionUID = 1L;
另一种是根据包名、类名、参数、返回值等因素,生成一个64位哈希字段,是一个唯一值。
private static final long serialVersionUID = -7684134237751913941L;
先来看序列化:
private static void serializableGirl() throws IOException {
Girl girl = new Girl();
girl.setBirth(1997);
girl.setName("Seas");
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(
new File("/Users/suyichen/workspace/Project/EclipseWork/girl.txt")));
oos.writeObject(girl);
System.out.println("序列化 success");
}
对象的输出流,将对象存入了指定的目录中。其中的 oos.writeObject(girl) 将girl这个对象先进行序列化,然后把序列化得到的字节序列写入到目标输出流中。
看,已经在我的指定目录下生成了文件。
再来看反序列化:
private static Girl deserializeGirl() throws Exception{
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(
new File("/Users/suyichen/workspace/Project/EclipseWork/girl.txt")));
Girl girl = (Girl) ois.readObject();
System.out.println("反序列化 success");
return girl;
}
对象输入流,ois.readObject() 方法将指定输入流中读取字节序列,再将它转化成为对象。
注意:恢复后的对象和之前的对象虽然内容是完全一样的,但两者并不是同一个对象。
serialVersionUID
当序列化的时候,系统会把当前类的 serialVersionUID 写入到序列化的文件中去;当反序列化的时候,系统再去检测 serialVersionUID ,如果它与当前类的 serialVersionUID 一致,则证明之前序列化时类的版本和现在反序列化的版本相同,这时候可以成功的反序列化。
因为上面说到,serialVersionUID 是根据根据包名、类名、参数、返回值等因素,生成一个64位哈希字段,是一个唯一值,一旦包名、类名、参数有变化,再次生成 serialVersionUID 的话,serialVersionUID 肯定是不同的。
假如我们指定了 serialVersionUID 的值,并且更改了一些成员变量,这时候,我们仍能反序列化成功,程序仍能最大限度恢复数据。
如果不去指定 serialVersionUID 的值,反序列时当前类有变化,那么系统就会自动重新计算当前类的 serialVersionUID ,这时候当前类的 serialVersionUID 就与序列化的数据中 serialVersionUID 不同,然后反序列化失败,程序crash。
不参与序列化的两种情况
1. 静态成员变量。因为静态成员变量是属于类的,而并非属于对象。
2. 用transient关键字标记的成员变量。
参考文献
https://www.jianshu.com/p/af2f0a4b03b5