前言
天天跟我说给我介绍对象对象,对象在哪里?哪里有对象?
你倒是把对象拿给我看看啊!
拿去拿去 :
{ "name": "小丽", "age": "22", "sex": "女" }
我去~
序列化概念
说到对象,是一个比较宽泛
的概念,简单的说,他就是类的一个实例,有状态和行为
,存活在内存中,一旦JVM停止运行,对象的状态也会丢失。
那么如何将这个对象当前状态进行一个记录,使其可以进行存储和传输
呢?这就要用到序列化了:
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程
比如一个User对象
,名字为小丽,年龄22,性别为女。现在要把这个User对象保存下来,不然要是这个对象被别人改成了男可咋办。
所以我们就可以把它当前的状态信息转化成一种固定的格式,比如json格式
:
{ "name": "小丽", "age": "22", "sex": "女" }
所以上述的例子就是一个序列化过程,本身这个User对象存活在内存中,是无法直接进行数据持久化的,所以我们需要一些序列化的方式让它可以进行保存传输:
比如xml、JSON、Protobuf、Serializable、Parcelable
,这些都是可以进行序列化的方式。
所以关于序列化我们就有很多问题了:
- 在java有Serializable的前提下,Android为什么设计出了Parcelable?
- Parcelable一定比Serializable快吗?
- 为什么Java提供了Serializable的序列化方式,而不是直接使用json或者xml?
- Serializable、Parcelable、Json等序列化方式我们该怎么选择?
带着这些问题,我们去看看序列化的世界。
Serializable
先说说Java中自带的序列化方式——Serializable
。
Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口
只要我们实现Serializable
接口,那么这个类就可以被ObjectOutputStream
转换为字节流,也就是进行了序列化。
使用
java:
public class User implements Serializable { private static final long serialVersionUID=519067123721561165l; private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } }
kotlin:
data class User( val id: Int ) : Serializable
serialVersionUID
这个变量如果不写,系统也会自动生成。它的作用在于标示这个数据对象的一致性。
当序列化的时候,系统会把当前类的serialVersionUID
写入序列化的文件中,当反序列化的时候会去检测这个serialVersionUID
,看他是否和当前类的serialVersionUID
一致,一样则可以正常反序列化,如果不一样就会报错了。
如果我们不写的话,在我们修改类的某些属性之后,serialVersionUID就会改变。
所以我们手动指定serialVersionUID
后,就能在修改类之后,让系统认识序列化的过程中标示这是同一个类,从而保证最大限度来恢复数据。
原理
在Serializable的注释中有提到,如果要想在序列化过程中做一些特殊的操作,可以实现这几个特殊方法:
writeObject()
,负责写入对象的特定类,以便相应的readObject方法可以恢复它readObject()
,负责从流中读取并恢复类字段
所以这两个方法其实就是Serializable
实现的关键。首先看看写入方法writeObject
(伪代码):
private void writeObject(){ //获取类的描述信息ObjectStreamClass(里面包含了类名称、类字段、serialVersionUID等,用到大量反射) desc = ObjectStreamClass.lookup(cl, true); //写入元数据TC_OBJECT,代表是一个新对象 bout.writeByte(TC_OBJECT); //写入描述信息(从父类写到子类) writeClassDesc(desc, false); //写入serialVersionUID,serialVersionUID为空的情况下,序列化机制就会调用一个函数根据类内部的属性等计算出一个hash值 getSerialVersionUID(); //执行JVM的序列化操作 defaultWriteFields(); } private void defaultWriteFields(Object obj, ObjectStreamClass desc){ //写入基本数据类型 bout.write(primVals, 0, primDataSize, false); //写入引用数据类型(又重新调用了writeObject方法) Object[] objVals = new Object[desc.getNumObjFields()]; for (int i = 0; i < objVals.length; i++) { writeObject(objVals[i],fields[numPrimFields + i].isUnshared()); } }
写入数据的流程基本就这些,可以看到Serializable序列化的过程,其实就是一个写入流的过程。然后就可以根据情况将二进制流保持为文件,或者包装成ByteArrayOutStream
写入到内存中进行传输。
所以Serializable
使用的范围比较广,可以作为文件保存下来,也可以作为二进制流对象用于内存中的传输。但是由于用到反射、IO,而且大量的临时变量会引起频繁的GC,所以效率不算高。
所以,为了提高在Android中对象传输的效率呢,Android就采用了新的序列化方式——Parcelable
。
Parcelable
Parcelable是Android为我们提供的序列化的接口,是为了解决Serializable在序列化的过程中消耗资源严重,而Android本身的内存比较紧缺的问题,但是用法较为繁琐,主要用于内存中数据的传输。
使用
java:
public class User implements Parcelable { private int id; protected User(Parcel in) { id = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); } @Override public int describeContents() { return 0; } public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; public int getId() { return id; } public void setId(int id) { this.id = id; } }
kotlin:
androidExtensions { experimental = true } @Parcelize data class User(val name: String) : Parcelable