简介
- 序列化:由于存在于内存中的变量都是暂时的,无法长期驻存,为了把对象的状态保持下来,把变量从内存中变成可存储或传输的过程就叫做序列化。
- 反序列化:反序列化恰恰是序列化的反向操作,反过来,把变量内容从序列化的对象重新读到内存里就叫做反序列化。
- Android中一个对象实现序列化操作,该类必须实现
Serializable
或Parcelable
接口。
Serializable
Serializable
是 java 提供的一个序列化接口,它是一个空接口,专门为对象提供标准的序列化和反序列化操作,使用Serializable实现类的序列化比较简单,只要在类声明中实现 Serializable 接口即可,同时强烈建议声明序列化标识serialVersionUID 。
serialVersionUID
是用来辅助序列化和反序列化过程的,原则上序列化后的对象中serialVersionUID只有和当前类的serialVersionUID相同才能够正常被反序列化,也就是说序列化与反序列化的serialVersionUID必须相同才能够使序列化操作成功。实际上我们不声明serialVersionUID也是可以的,因为在序列化过程中会自动生成一个serialVersionUID来标识序列化对象。
import java.io.Serializable;
public class Person implements Serializable {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) throws Exception {
Person p = new Person();
p.setId(1);
p.setName("Tom");
p.setAge(25);
//序列化过程
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("p.txt"));
oos.writeObject(p);
oos.close();
//反序列化过程
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("p.txt"));
Person p2 = (Person) ois.readObject();
System.out.println(p2);
ois.close();
}
自定义序列化过程
writeReplace()
:序列化时,首先系统会先调用writeReplace
方法,在这个阶段,可以进行自己操作,将需要进行序列化的对象换成我们指定的对象, 一般很少重写该方法。writeObject()
:接着系统将调用writeObject
方法,来将对象中的属性一个个进行序列化,我们可以在这个方法中控制住哪些属性需要序列化。readObject()
:反序列化时,系统会调用readObject
方法,将我们刚刚在writeObject
方法序列化好的属性,反序列化回来。然后通过readResolve
方法,我们也可以指定系统返回给我们特定的对象可以不是writeReplace
序列化时的对象,可以指定其他对象.readResolve()
:通过readResolve
方法,我们也可以指定系统返回给我们特定的对象可以不是writeReplace
序列化时的对象,可以指定其他对象,一般很少重写该方法。
package com.example.myapp;
import java.io.IOException;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class Person implements Serializable {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
/**
* 序列化时,
* 首先系统会先调用writeReplace方法,在这个阶段,
* 可以进行自己操作,将需要进行序列化的对象换成我们指定的对象.
* 一般很少重写该方法
*/
private Object writeReplace() throws ObjectStreamException {
System.out.println("writeReplace invoked");
return this;
}
/**
* 接着系统将调用writeObject方法,
* 来将对象中的属性一个个进行序列化,
* 我们可以在这个方法中控制住哪些属性需要序列化.
* 这里只序列化name属性
*/
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
System.out.println("writeObject invoked");
out.writeObject(this.name == null ? "默认值" : this.name);
out.writeObject(this.age);
out.writeObject(this.id);
}
/**
* 反序列化时,系统会调用readObject方法,将我们刚刚在writeObject方法序列化好的属性,
* 反序列化回来.然后通过readResolve方法,我们也可以指定系统返回给我们特定的对象
* 可以不是writeReplace序列化时的对象,可以指定其他对象.
*/
private void readObject(java.io.ObjectInputStream in) throws IOException,
ClassNotFoundException {
System.out.println("readObject invoked");
this.name = (String) in.readObject();
this.age = (int) in.readObject();
this.id = (int) in.readObject();
System.out.println("name:" + name);
}
/**
* 通过readResolve方法,我们也可以指定系统返回给我们特定的对象
* 可以不是writeReplace序列化时的对象,可以指定其他对象.
* 一般很少重写该方法
*/
private Object readResolve() throws ObjectStreamException {
System.out.println("readResolve invoked");
return this;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) throws Exception {
Person p = new Person();
p.setId(1);
// p.setName("Tom"); // 不设置
p.setAge(25);
//序列化过程
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("p.txt"));
oos.writeObject(p);
oos.close();
//反序列化过程
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("p.txt"));
Person p2 = (Person) ois.readObject();
System.out.println(p2);
ois.close();
}
输出信息:
writeReplace invoked
writeObject invoked
readObject invoked
got name:默认值
readResolve invoked
Person{id=1, name='默认值', age=25}
Parcelable
鉴于Serializable
在内存序列化上开销比较大,而内存资源属于android系统中的稀有资源(android系统分配给每个应用的内存开销都是有限的),为此android中提供了Parcelable
接口来实现序列化操作,Parcelable
的性能比Serializable
好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable
,如通过Intent在activity间传输数据。
package com.example.myapp.parcelable;
import android.os.Parcel;
import android.os.Parcelable;
public class User implements Parcelable {
public int id;
public String name;
public int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
/**
* 当前对象的内容描述,一般返回0即可
*/
@Override
public int describeContents() {
return 0;
}
/**
* 将当前对象写入序列化结构中
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);
dest.writeString(this.name);
dest.writeInt(this.age);
}
/**
* public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。
*/
public static final Creator<User> CREATOR = new Creator<User>() {
/**
* 从序列化后的对象中创建原始对象
*/
@Override
public User createFromParcel(Parcel source) {
User user = new User();
user.id = source.readInt();
user.name = source.readString();
user.age = source.readInt();
return user;
}
/**
* 创建指定长度的原始对象数组
*/
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}
Parcelable 与 Serializable 区别
- 在使用内存的时候,
Parcelable
比Serializable
性能高,所以推荐使用Parcelable
。 Serializable
在序列化的时候会产生大量的临时变量,从而引起频繁的GC。Parcelable
不能使用在要将数据存储在磁盘上的情况,因为Parcelable
不能很好的保证数据的 持续性在外界有变化的情况下。尽管Serializable
效率低点,但此时还是建议使用Serializable
。