1.实现Serializable接口
最简单的方式就是让类实现Serializable接口,然后啥事都不干。
另一种方式是不仅让类实现Serializable接口,并声明一个serialVersionUID。
两种方式的区别:
序列化得时候会把当前类的serivalVersionUID写入序列化的文件中,当系统反序列化时会计算当前类的 serialVersionUID 和序列号文件中的 serialVersionUID 值是否相同,不同则反序列化失败
- 若不指定它的值,serialVersionUID 的值就会随着类文件的改动(当类有所改变的时候系统会重新计算当前类的hash 值,并把它赋给serialVersionUID,此时相当于serialVersionUID和之前序列化数据中的serialVersionUID 不一致,)也会跟着变化,导致反序列化失败。
- 若指定它的值,即便版本发生变化,也可以强制令系统认为版本一致,避免反序列化失败。
需要注意的点有:
1)序列化之前的对象和反序列化后的对象的内容是完全一样的,但是两者并不是同一个对象。
2)静态成员变量属于类不属于对象,所以不参与序列化过程
3)
声明为 transient 的成员变量不参与序列化过程(当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化;当对象被反序列化时(从源文件读取字节序列进行重构),这样的实例变量值不会被持久化和恢复。)
4)默认的序列化过程是可以改变的,可以通过重写类的 writeObject 和 readObject 方法重写系统的默认序列化过程
2实现Parcelable 接口
通过实现该接口,该类对象就可以实现序列化并可以通过 Intent 和 Binder 传递
Parcelable接口定义:
public interface Parcelable
{
//内容描述接口,默认返回0即可,如果含有文件描述符,返回1.
public int describeContents();
//写入接口函数,打包
public void writeToParcel(Parcel dest, int flags);
//读取接口,目的是要从Parcel中构造一个实现了Parcelable的类的实例处理。因为实现类在这里还是不可知的,所以需要用到模板的方式,继承类名通过模板参数传入
//为了能够实现模板参数的传入,这里定义Creator嵌入接口,内含两个接口函数分别返回单个和多个继承类实例
public interface Creator<T>
{
public T createFromParcel(Parcel source);
public T[] newArray(int size);
}
}
从parcelable接口定义中,我们可以看到,实现parcelable接口,需要我们实现下面几个方法:
1.describeContents方法。内容接口描述,默认返回0就可以;
2.writeToParcel 方法。该方法将类的数据写入外部提供的Parcel中.即打包需要传递的数据到Parcel容器保存,以便从parcel容器获取数据,该方法声明如下:
writeToParcel (Parcel dest, int flags) 具体参数含义见javadoc
3.静态的Parcelable.Creator接口,本接口有两个方法:
createFromParcel(Parcel in) 从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层。
newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话(return new T[size])即可。方法是供外部类反序列化本类数组使用。
1 public class Person implements Parcelable{ 2 3 // 成员变量 4 private int id; 5 private String name; 6 7 // 1.必须实现Parcelable.Creator接口,否则在获取Person数据的时候,会报错,如下: 8 // android.os.BadParcelableException: 9 // Parcelable protocol requires a Parcelable.Creator object called CREATOR on class com.um.demo.Person 10 // 2.这个接口实现了从Percel容器读取Person数据,并返回Person对象给逻辑层使用 11 // 3.实现Parcelable.Creator接口对象名必须为CREATOR,不如同样会报错上面所提到的错; 12 // 4.在读取Parcel容器里的数据事,必须按成员变量声明的顺序读取数据,不然会出现获取数据出错 13 // 5.反序列化对象 14 public static final Parcelable.Creator<Person> CREATOR = new Creator(){ 15 16 @Override 17 public Person createFromParcel(Parcel source) { 18 // TODO Auto-generated method stub 19 // 必须按成员变量声明的顺序读取数据,不然会出现获取数据出错 20 Person p = new Person(); 21 p.setId(source.readInt()); 22 p.setName(source.readString()); 23 return p; 24 } 25 26 @Override 27 public Person[] newArray(int size) { 28 // TODO Auto-generated method stub 29 return new Person[size]; 30 } 31 }; 32 33 public int getId() { 34 return id; 35 } 36 37 public void setId(int id) { 38 this.id = id; 39 } 40 41 public String getName() { 42 return name; 43 } 44 45 public void setName(String name) { 46 this.name = name; 47 } 48 49 @Override 50 public int describeContents() { 51 // TODO Auto-generated method stub 52 return 0; 53 } 54 55 @Override 56 public void writeToParcel(Parcel dest, int flags) { 57 // TODO Auto-generated method stub 58 // 1.必须按成员变量声明的顺序封装数据,不然会出现获取数据出错 59 // 2.序列化对象 60 dest.writeInt(id); 61 dest.writeString(name); 62 } 63 }
3两者的区别
Serializable是Java中的序列化接口,使用起来简单但是开销很大,序列化和反序列化需要大量的I/O操作。
Parcelable是Android中的序列化方式,它的缺点是使用起来稍微麻烦些,但是它很高效,这是Android推荐的序列化方式,因此我们要首选Parcelable。
Parcelable主要用在内存序列化上;通过Parcelable将对象序列到存储设备中或者将对象序列化后通过网络传输也都是可以的但是这个过程稍微复杂,因此这两种情况下可以使用Serializable接口序列化。