您的位置:首页 > 其它

一篇文章弄懂Parcelable和Serializable

2018-08-03 16:35 387 查看

阅读完本篇文章会知道如下三点:

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

[code]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 +"]";
}

}

注意里面是有一个静态变量。下面将对象输出到一个文件中:

[code]/**
* 序列化函数
*
*
*/
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();
}

}

实践结果是可以的。

下面进行反序列化:

[code]	/**
* 反序列化
*
*
* @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类型的字段

例子如下:

[code]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中的序列化方式,虽然使用起来麻烦一点,但是效率高。实际上如何打包和解包的工作自己定义,序列化的这些操作完全交个底层实现。

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: