序列化作用
- 将对象持久化到存储设备上
- 让对象能够在网络中传输
- 让对象能够在IPC(进程间通信)中进行传输,作为进程间通信的数据载体
序列化方式
在Android中要想实现序列化有两种方式,分别是Serializable和Parcelable,其中Serializable是java提供的一个序列化接口,Parcelable是Android中提供的新的序列化接口。
Serializable
Serializable是一个空接口,要想通过Serializable实现序列化其实非常简单,只需要实现这个接口并定义一个serialVersionUID即可(serialVersionUID并不是必须要定义,这个在下文中说明)。
这里定义一个user类实现Serializable接口,如下所示。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
}
要想实现对象的序列化,我们需要借助Java IO流中的ObjectOutputStream和ObjectInputStream来完成对象的序列化和发序列化。
具体实现如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) {
write();
// read();
}
/**
* 序列化
*/
public static void write(){
try {
File file = new File("src/user.txt");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
User user = new User("jack", 20);
System.out.println(user);
oos.writeObject(user);
oos.flush();
oos.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 反序列化
*/
public static void read(){
try {
File file = new File("src/user.txt");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
User user = (User) ois.readObject();
System.out.println(user);
ois.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
上述的代码就是通过Serializable实现序列化的完整过程。
serialVersionUID的作用
在上文中我们曾提到在序列化时可以不用定义serialVersionUID,如果不定义serialVersionUID这对于对象的序列化是没有任何影响的,但是会对对象的反序列化过程造成影响。
通常情况下,只有当序列化的数据的serialVersionUID和当前类的serialVersionUID相同时,数据才会被反序列化。这里我们可以通过上面的demo验证这个问题,首先将User类中的serialVersionUID设置成1L,然后对该对象进行序列化,然后将serialVersionUID修改成2L,然后进行反序列化。发现此时程序会抛出异常:
java.io.InvalidClassException: User; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
这里也特别提出,静态变量和被transient关键字修饰的字段不会参与序列化。
Parcelable
Parcelable是Android提供的一个序列化接口,通过Parcelable实现序列化有一下几个步骤:
- 实现Parcelable接口
- 重写writeToParcel方法,该方法主要用于将对象的序列化数据存储到Parcel对象中。Parcel封装了一系列的write和read方法用于序列化和反序列化。
- 重写describeContents方法,该方法默认返回0即可。(当存在文件描述符时返回1)
- 实例化静态内部对象CREATOR实现接口Parcelable.Creator。用于反序列化。
具体用法如下:
package com.zhangke.test;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by zhangke on 2017/2/8.
*/
public class User implements Parcelable {
private String username;
private int age;
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];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(username);
dest.writeInt(age);
}
public User(Parcel parcel) {
username = parcel.readString();
age = parcel.readInt();
}
}
两种方式的比较
Serializable实现序列化简单快捷,只需要实现一个接口即可。但是,通过该方式实现序列化及反序列化需要大量的IO操作,这样造成的开销较大;同时,通过Serializable底层要借助反射来完成序列化,这种方式会导致序列化的过程较慢并在序列化的过程中创建许多的临时对象,容易触发垃圾回收。
Parcelable实现序列化优点是效率极高,缺点是实现细节比较复杂。
Parcelable主要用于对象的内存序列化上,虽然也能够通过Parcelable将对象序列化到存储设备中或将数据序列化后进行网络传输,但是这个过程建议使用Seriaizalbe更加方便。