定义
在Java中的对象序列化指的是将一个Java对象所描述的内容转换为可以存储和传输的形式的过程,通常是将对象转换为字节序列;反序列化就是相反的过程,将字节序列恢复成对象
序列化用途
- 将对象的字节序列持久化到硬盘中,通常是存放在文件里
- 进程间通信的时候不能直接传输对象,需将对象以字节序列的形式传输
注意
- 类里的静态成员变量不属于对象,不会参加序列化
- 被transient关键字修饰的成员变量不会参与序列化,因为在反序列化的过程中,transient修饰的变量的值会被设置为初始值,比如int型变量会被设置为0,引用型的会被置为null
序列化方法
在Android中实现对象序列化的方法有两种
前言:
其实有一种伪序列化/反序列化方法,就是使用DataoutputStream/DataInputStream,只不过这种方法需要开发者按顺序一个字段一个字段的进行操作,很反人类;并且不能支持复杂类型数据
try {
//序列化
DataOutputStream dos = new DataOutputStream(new FileOutputStream(filePath));
OperatorVo bean = new OperatorVo();
bean.setAge(1);
bean.setOpername("boy");
dos.writeInt(bean.getAge());
dos.writeUTF(bean.getOpername());
dos.close();
//反序列化
DataInputStream dis = new DataInputStream(new FileInputStream(filePath));
bean.setAge(dis.readInt());
bean.setOpername(dis.readUTF());
dis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
第一种:实现Serializable接口
Serializable:这是Java提供的一个为对象提供标准的序列化和反序列化的接口,是一个空的接口,没有声明任何方法,开发者只需为自己的对象实现这个接口就行了
这样就能标记这个对象,虚拟机执行序列化指令,就判定该对象可以操作,继而使用ObjectOutputStream进行序列化;反序列化亦是如此
SerialiVersionUID
通常我们实现这个接口的时候,最好指定要给SerialiVersionUID,这是序列化版本号;在进行序列化的时候,会把这个值写入序列化文件,如果没有显示的指定,那么编译器就会自动生成一个;这样在进行反序列化的时候就会检测文件中的SerialiVersionUID与这个类当前的SerialiVersionUID是否相同,如果不同就会反序列化失败,我们在类中增加或者减少字段的时候,自动生成的SerialiVersionUID也会变化,反序列化的时候也会失败;所以开发者最好显示的指定一个SerialiVersionUID
当然你不指定的话不影响序列化,但是可能会影响反序列化
示例
public class Boy implements Serializable{
private static final long serialVersionUID = 3171346693605985494L;
private String name;
private String age;
.......
}
//序列化
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath));
out.writeObject(new Boy());
out.close();
} catch (IOException e) {
e.printStackTrace();
}
//反序列化
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(filePath));
Boyp = (Boy)in.readObject();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
如果序列化对象里还引用了其它对象,那writeObject方法会进行递归序列化
第二种:实现Parcelable接口
Parcelable:是Android提供的序列化接口,通过Parcel写入和恢复数据。
Parcel:它是一个容器,他可以包含数据或者是对象引用,并且能够用于Binder的传输,同时支持序列化以及跨进程之后进行反序列化
示例
public class Boy implements Parcelable{
private String name;
private int age;
public Boy() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 返回当前对象的内容描述,如果含有文件描述符,返回1
* 通常返回0
*/
@Override
public int describeContents() {
return 0;
}
/**
* 序列化功能由该方法完成,主要是通过Parcel对象的一系列write方法,将本对象的值写到Parcel对象
* Parcel类似于使用ObjectOutputStream
* 第二个参数有两种值,0和1
* 1:如果当前对象需要作为返回值返回,就不能立刻释放资源
* 通常情况下为0
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(age);
dest.writeString(name);
}
/**
* 在使用AIDL进行IPC需要这个方法
* 默认生成的模板类的对象只支持为 in 的定向 tag,因为只有writeToParcel方法
* 如果要支持为 out 或者 inout 的定向 tag 的话,还需要实现 readFromParcel() 方法
* 参数是一个Parcel,用它来存储与传输数据
* 注意,此处的读值顺序应当是和writeToParcel()方法中一致的
* @param dest
*/
public void readFromParcel(Parcel dest){
age = dest.readInt();
name = dest.readString();
}
public Boy(Parcel parcel){
age = parcel.readInt();
name = parcel.readString();
}
/**
* public static final 三个词一个都不能少,并且CREATOR这个名词也不能改变,必须大写
*
*/
public static final Creator<Boy> CREATOR = new Creator<Boy>() {
/**
* 创建一个类型为T,长度为size的数组,供外部类反序列化本类数组使用
*/
@Override
public Boy[] newArray(int size) {
return new Boy[size];
}
/**
* 反序列化 读取顺序要与序列化顺序一致
* 从Parcel容器中读取数据,封装成对象返回给开发者
*/
@Override
public Boy createFromParcel(Parcel source) {
return new Boy(source);
}
};
}
对比:
- Serializable实现简单,而Parcelable需要更多的操作
- Serializable是Java的接口,使用时需要大量I/O操作,开销大,常用于持久化对象到本地;Parcelable效率高,使用麻烦,常用于内存级别的序列化,比如组件间的传输,网络中传输数据,IPC中数据传输等
- Parcelable为了效率没有考虑更多的兼容性,所以数据持久化操作使用Serializable