一篇文章弄懂Parcelable和Serializable
阅读完本篇文章会知道如下三点:
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中的序列化方式,虽然使用起来麻烦一点,但是效率高。实际上如何打包和解包的工作自己定义,序列化的这些操作完全交个底层实现。
- 另一篇关于 Serializable 和 Parcelable 对比的文章
- 一篇文章弄懂Spring MVC的参数绑定
- 一篇文章弄懂Android自定义viewgroup的相关难点
- iOS世界时间,NSDateFormatter一篇文章全弄懂
- 一篇文章让你彻底弄懂WinForm GDI 编程基本原理
- 一篇文章彻底弄懂Base64编码原理
- 一篇文章弄懂Linux磁盘和磁盘分区
- 一篇文章让你彻底弄懂JS的事件冒泡和事件捕获
- 一篇文章弄懂CSS3的content属性
- 学C++不得不看的一篇文章[转]
- 今天开始,每天写一篇文章,让纪录成为一种习惯!!
- Android:学习AIDL,这一篇文章就够了(上)
- 上一篇文章、下一篇文章实现
- 今天有些失落,给大家分享一篇文章,希望咱们都能以此明心智。
- [转贴]一篇关于sizeof的精辟文章
- 一篇有关软件架构设计的文章(转自软件工程专家网,注意其中有关性能的内容)
- Serializable 和 Parcelable 区别 以及 为什么要序列化?
- Android自学--一篇文章基本掌握所有的常用View组件
- 今天学习了一篇大牛的整合文章,记的笔记
- Intent传递对象的两种方法Serializable 和 Parcelable