阅读完本篇文章会知道如下三点:
1.Parcelable和Serializable的作用?
2.通过例子来区分Parcelable和Serializable,两者的流程是?
3.两者的区别?
首先知道什么是序列化:
官方的概念是:
序列化:把对象转换为字节序列的过程
反序列化:把字节序列恢复为对象的过程
通俗的理解:
序列化就是将对象的状态信息转换为可以存储或传输的形式过程。在java中使用Serializable关键字实现序列化。序列化的目的是以某种存储形式使自定义对象持久化,简单的理解就是:将对象从一个地方传递到另一个地方。那么这时我们可以得知需要序列化的情况:
1.当我们把内存对象状态保存到一个文件中或者数据库中
2.当用套接字在网络上传送对象
3.当通过RMI传输对象
后面两种也是网上所查。
1.Parcelable和Serializable的作用
Parcelable是Android独有提供的系列化接口,而Serializable是java提供的序列化接口。Parcelable只能在Android中使用,Serializable可以在使用java语言的地方使用。
2.通过例子来区分Parcelable和Serializable,两者的流程是?
下面分别举出两个例子:
Serializable
public class People implements Serializable{
//名字
private String name;
//年龄
private int age;
//静态变量
private static String test = "1234";
private static final long serialVersionUID = 1L;
public People() {
System.out.println("进入默认的构造方法");
}
private People(int age) {
System.out.println("私有构造方法:年龄"+age);
}
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 "People [name=" + name + ", age=" + age + ",test="+ test +"]";
}
}
注意里面是有一个静态变量。下面将对象输出到一个文件中:
/**
* 序列化函数
*
*
*/
private static void serializePeople() {
People people = new People();
people.setAge(24);
people.setName("paul");
try {
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream(new File("d:/people.txt")));
output.writeObject(people);
output.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
实践结果是可以的。
下面进行反序列化:
/**
* 反序列化
*
*
* @return 返回对象
*/
private static People deseriallizePeople() {
try {
ObjectInputStream oInput = new ObjectInputStream(new FileInputStream(new File("d:/people.txt")));
try {
People person = (People) oInput.readObject();
return person;
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
执行函数结果显示:
结果显示反序列化成功,但是上面说过有一个静态变量,现在我们不知道是否到底有没有序列化,下面验证一下:下面将test改为:private static String test = "12";然后反序列一下看看结果。
可以发现之前设定的test值1234并没有将之后设定的test值12给覆盖到,也就是说静态变量不参与序列化!
另外注意到类里面有serialVersionUID这个值。
这里重点说一下:其实不指定serialVersionUID也是可以实现序列化,那么加上这个字段又是有什么作用呢?其实这个serialVersionUID是用来辅助序列化和反序列化过程的,因为原则上序列化后的数据中serialVersionUID只有和当前类的serialVersionUID相同时才能够正常地被反序列化。
serialVersionUID的详细工作机制是:
1.序列化的时候系统会去把当前类的serialVersionUID写入序列化的文件中(也有可能是其他中介)
2.当反序列化的时候系统会检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致的时候就说明序列化的类的版本和当前类的版本是相同的,这个时候就可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换。这时候是无法正常反序列化的。
那么serialVersionUID这个值应该怎么指定?
一般来说,我们应该手动指定serialVersionUID的值,如1L,如果不手动指定serialVersionUID的值,反序列化时当前类有所改变,如增加或者删除某些成员变量,系统就会重新计算当前类的hash值并赋值给serialVersionUID,这个时候当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致于是就有反序列化失败。
当手动指定它的值,就很大程度上避免了反序列化过程的失败,比如版本迭代中,删除或者增加一些成员变量,这时候还是反序列化成功。
因此通常给serialVersionUID指定为1L就可以了。
注意:如果类发生结构上的变化上时:如修改了类名,修改了成员变量的类型,虽然serialVersionUID验证通过了,但是反序列化过程还是会失败的。
下面讲讲如何自定义一个类让其实现Parcelable?
自定义一个类让其实现Parcelable接口,主要分为三部分:
1.重写该接口的writeToParcel(Paecel out,int flags)
2.重写describeContents()
3.添加一个Parcelable.Creator类型的字段
例子如下:
public class PeopleParcel implements Parcelable {
public String name;
public boolean isMan;
public int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isMan() {
return isMan;
}
public PeopleParcel(String name, boolean isMan, int age) {
this.name = name;
this.isMan = isMan;
this.age = age;
}
public void setMan(boolean man) {
isMan = man;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 实现反序列化,需要有一个参数类型为Parcel的构造方法
* @param in
*/
private PeopleParcel(Parcel in){
name = in.readString();
age = in.readInt();
isMan = in.readInt() == 1;
}
public static final Creator<PeopleParcel> CREATOR = new Creator<PeopleParcel>(){
/**
* 根据Parcel中来创建一个原始对象
* @param source
* @return
*/
@Override
public PeopleParcel createFromParcel(Parcel source) {
return new PeopleParcel(source);
}
/**
* 返回指定长度的原始对象数组
* @param size
* @return
*/
@Override
public PeopleParcel[] newArray(int size) {
return new PeopleParcel[size];
}
};
/**
* 返回当前对象的内容描述。如果含有文件描述符,返回1,否则返回0,几乎所有情况都是返回0
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 调用Parcel的一系列方法来把数据写入Parcel中
* @param dest
* @param flags
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
dest.writeInt(isMan ? 1 : 0);
}
}
3.两者的区别?
Android中的序列化的例子就是上面所示。
在Android中两者都能实现序列化并能用Intent间的数据传递,难么究竟还有什么区别呢?
1.Serializable是java中的序列化接口,使用起来简单但是开销大,因为序列化过程需要大量的I/O操作
2.Pacelable是Android中的序列化方式,虽然使用起来麻烦一点,但是效率高。实际上如何打包和解包的工作自己定义,序列化的这些操作完全交个底层实现。