序列Parcelable,Serializable的区别?(阿里)
详细讲解
架构师筑基必备技能《NDK专题-JNI实战篇》
这道题想考察什么?
掌握序列化接口实现原理,针对不同场景在工作中合理运用
考察的知识点
Parcelable原理
Serializable原理
考生应该如何回答
序列化 是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。以后,可以通过从存储区中读取或反序列化对象的状态,重新创建该对象。Serializable是Java提供的序列化机制,而 Parcelable则是Android提供的适合于内存中进行传输的序列化方式。
Serializable
Serializable是Java中提供的一个 序列化接口,然而这个接口并没有任何方法需要实现者实现。
public interface Serializable {
}
这表示Serializable接口知识用来标识当前类可以被序列化与反序列化。
基本使用
实现了Serializable接口的类对象能够通过 ObjectOutputStream 将需要序列化数据写入到流中,因为 Java IO 是一种装饰者模式,因此可以通过 ObjectOutStream 包装 FileOutStream 将数据写入到文件中或者包装 ByteArrayOutStream 将数据写入到内存中。也可以通过 ObjectInputStream 从磁盘或者内存读取数据然后转化为指定的对象即可(反序列化)。
try {
TestBean serialization = new TestBean("a","b");
//序列化
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("/path/xxx"));
os.writeObject(serialization);
//反序列化
ObjectInputStream is = new ObjectInputStream(new FileInputStream("/path/xxx"));
TestBean deserialization = (TestBean) is.readObject();
} catch (IOException e) {
e.printStackTrace();
}
实现了Serializable接口的类中成员属性除基本数据类型外,即引用类型,也需要实现Serializable接口,否则将在序列化时抛出java.io.NotSerializableException
异常。
public class TestBean implements Serializable {
private String name;
private String pwd;
//Gson未实现Serializable接口,TestBean实例对象在序列化时抛出NotSerializableException
private Gson gson = new Gson();
public TestBean(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public TestBean() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
因此,如果需要对某个类进行序列化时,对于类中不需要进行序列化与反序列化的属性,可以使用 transient 关键字声明。
private transient Gson gson = new Gson();
serialVersionUID
在实现了Serializable接口的类中,应该为此类定义一个serialVersionUID属性:
private static final long serialVersionUID = 1L;
虽然不定义程序依然能够正常运行。 但是Java序列化的机制是通过判断类的serialVersionUID来验证的版本一致的。
序列化操作时会把当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会自动检测文件中的serialVersionUID,判断它是否与当前类中的serialVersionUID一致。如果一致说明序列化文件的版本与当前类的版本是一样的,可以反序列化成功,否则就失败。
例如在A程序中TestBean类的serialVersionUID=1L,而在程序B中TestBean类的serialVersionUID=2L。 那么A程序序列化输出的数据,在B程序中就无法反序列化为TestBean对象。
Parcelable
Parcelable是Android为我们提供的序列化的接口。相对于Java的Serializable,Parcelable的使用稍显复杂:
public class TestBean implements Parcelable {
private String name;
private String pwd;
public TestBean(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public TestBean() {
}
public int describeContents() {
return 0;
}
//序列化
public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeString(pwd);
}
public static final Parcelable.Creator<TestBean> CREATOR
= new Parcelable.Creator<TestBean>(){
public TestBean createFromParcel(Parcel in) {
return new TestBean(in);
}
public MyParcelable[] newArray(int size) {
return new TestBean[size];
}
}
private TestBean(Parcel in) {
// 反序列化:按序列化顺序读取
name = in.readString();
pwd = in.readString();
}
}
Parcelable接口的实现类是通过Parcel写入和恢复数据的,并且必须要有一个非空的静态变量 CREATOR。
Parcel
Parcel其实就是一个数据载体,可以将序列化之后的数据写入到一个共享内存中,其他进程通过Parcel可以从这块共享内存中读出字节流,并反序列化成对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mbJWICkG-1686553360030)(images\parcel.png)]
Parcel可以读写原始数据类型,也可以读写实现了Parcelable对象。
因此Parcelable实现序列化的原理就是将一个完整的对象进行分解,而分解后的每一部分都是基本数据类型或者其他实现了Parcelable/Serializable的类型,从而实现传递对象的功能。
区别
Parcelable和Serializable都是实现序列化并且在Android中都可以使用Intent进行数据传递。Serializable是Java的实现方式,实现过程中会频繁的IO操作,但是实现方式简单。而Parcelable是Android提供的方式,效率比较高(号称比Serializable快10倍),但是实现起来复杂一些 。
如果只需要在内存中进行数据传输是,序列化应该选择Parcelable,而如果需要存储到设备或者网络传输上应该选择Serializable。这是因为Android不同版本Parcelable数据规则可能不同,所以不推荐使用Parcelable进行数据持久化。