Serializable和Parcelable的解析与使用



很多情况下,我们都需要将一些数据对象序列化,因为要想操作系统识别对象数据,就必须要序列化成二进制数组,这在我们进行存储文件,操作文件数据,或者进行进程间的数据交流......很多底层的操作都是必须的。java和android都提供了可序列化的接口声明, 在java中提供了Serializable接口,android中提供了Parcelable接口,这两者都可以用于声明一个对象,表明其是可序列化的。其中,前者实现起来简单,但是开销很大(因为底层实现进行了大量的读写操作)。后者实现复杂但是开销小,具体使用哪个需要看情况。下面分别了解一下这两个接口。

一、Serializable的解析与使用:

Serializabl是一个序列化接口,凡是实现了这个接口的类,都可以通过ObjectOutputStream(一种特殊的输出流,可以将java对象像基本类型一样序列化到存储设备)来序列化,同时也可以通过ObjectInputStream来反序列化。当我们使用实现了这个接口的类时,对此类结构的修改要特别小心,因为很多操作都是不安全的,这会导致反序列化失败,后面会带来详细的解释。为了提高反序列化操作的成功率,首先需要了解一个静态常量serialVersionUID,一般来说,这个常量是系统根据类的结构给出的hash值,当类发生变化时,对应的serialVersionUID也会发生改变。但是这个常量不是一定要使用系统为我们生成的值,通常为了提高反序列化的成功率,人为的指定serialVersionUID的值是被建议的。为了使用这个接口,需要了解以下几点:
1、serialVersionUID是随着类的结构的改变而改变的,如果我们没有明确指出这个值,那么在反序列化的时候,系统会先计算该类的serialVersionUID值,然后和序列化对象的serialVersionUID的值进行对比,如果两者不相同,就会序列化失败。
2、如果明确指出了serialVersionUID的值(系统在反序列化时就不会重新计算该值,而是使用类里面声明的值 ),那么只要不是破坏性的类的结构的改变,就可以成功反序列化。比如修改了类名,或者某个变量的类型,那么就会导致类的结构发生根本性的改变。但如果修改的是某个变量的类型,修改后的类型与原先的类型是兼容的(比如,int修改为float),那么反序列化仍然可以成功。
3、transien标志的属性不参与序列化。还有就是静态变量也不参与序列化(因为静态变量属于类而不属于某个具体的对象)。
4、反序列化回来的对象并不是原来的对象,只是数据一样,但是是新的一个实例对象。
Serializable的使用比较简单,接下来我们通过一个例子并结合控制台的输出结果,来印证刚刚得出的论点。 首先是User类,这是一个实现了Serializable接口的类:
import java.io.Serializable;/** *  * @author Myy * */public class User implements Serializable {/** * 指定serialVersionUID值 */private static final long serialVersionUID = 2462633439866771574L;private String name;private int age;private boolean isBoy;private transient String school; // transient标记的属性不参与序列化public static int height = 220; // 静态成员也不参与序列化public User(String name, int age, boolean isBoy) {super();this.name = name;this.age = age;this.isBoy = isBoy;}public User(String name, int age, boolean isBoy, String school) {super();this.name = name;this.age = age;this.isBoy = isBoy;this.school = school;}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;}public boolean isSex() {return isBoy;}public void setBoy(boolean isBoy) {this.isBoy = isBoy;}@Overridepublic String toString() {return super.toString() + ",name=" + name + ",age=" + age + ",isBoy=" + isBoy + ",school=" + school + ",height="+ height;}public String getSchool() {return school;}public void setSchool(String school) {this.school = school;}}





然后是TestSerializable类,这个类用于将对象序列化然后再反序列化,并通过控制台打印出来。
import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class TestSerializable {private static String path = "D:\\text.txt";public static void main(String[] args) {User user = new User("hy", 22, true,"社会大学");writeToFile(user);System.out.println(user.toString());User newUser = readFromFile();System.out.println(newUser.toString());}private static User readFromFile() {User user = null;ObjectInputStream ois = null;try {ois = new ObjectInputStream(new FileInputStream(new File(path)));try {user = (User) ois.readObject();} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return user;}private static void writeToFile(User user) {ObjectOutputStream oops = null;try {oops = new ObjectOutputStream(new FileOutputStream(new File(path)));oops.writeObject(user);} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally {try {oops.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

接下来是打印结果: 序列化数据:User@5c647e05(对象hash值),name=hy,age=22,isBoy=true,school=社会大学,height=220
反序列化数据:User@75b84c92(对象hash值),name=hy,age=22,isBoy=true,school=null,height=220

从序列化和反序列化出来的结果,可以得出,school字段是不参与序列化的,因为被标志了transien标签。同时,序列化和反序列化的结果虽然数据一样(school因为未被序列化,所以反序列化的时候由于没有值而被置为null),但是并不是同一个对象(对象的hash值不一样)。

二、Parcelable的解析与使用

Parcelable接口是android提供的序列化对象的接口,凡是实现了Parceleable接口的类,都可以序列化数据到Parcel(android中的一种高效率进程间通信的数据容器,可以在进程间建立起连接通道,通过Parcel,可以将任意对象序列化并传输到远程进程。例如Bundle就是一个很好的例子)里面,并且可以从Parcel恢复数据。接下来讲解一下Parcelable接口里面的重要的方法:
1、writeToParcel(Parcel out, int flags),将对象数据系列化。其中flags标志是否需要作为返回值返回,0表示不需要,1表示需要,此时不能立即释放资源。正常情况下传值0。
2、describeContents(),一般情况下都返回0,当含有文件描述符的时候就返回1.
3、必须重写Parcelable.Creatot接口,且变量名称是CREATOR.
标准的实现方法如下:
* public class MyParcelable implements Parcelable { *     private int mData; * *     public int describeContents() { *         return 0; *     } * *     public void writeToParcel(Parcel out, int flags) { *         out.writeInt(mData); *     } * *     public static final Parcelable.Creator CREATOR *             = new Parcelable.Creator() { *         public MyParcelable createFromParcel(Parcel in) { *             return new MyParcelable(in); *         } * *         public MyParcelable[] newArray(int size) { *             return new MyParcelable[size]; *         } *     }; *      *     private MyParcelable(Parcel in) { *         mData = in.readInt(); *     } * }



更多相关文章

  1. Android(安卓)的网络编程(3)-HttpURLConnection接口
  2. OpenMax多媒体引擎
  3. Android四大核心组件之一-----Service(服务)的基本知识
  4. “刨根问底”之Android(安卓)消息机制
  5. Android(安卓)Matrix Riot (IM)SDK 集成专栏(登录注册)
  6. Activity的启动流程分析
  7. Android中抓取手机视频流数据
  8. Android(安卓)数据Parcel序列化过程源码分析
  9. 浅谈Android系统开发中LOG的使用

随机推荐

  1. Android之Bundle类
  2. 使用Tensorflow部署手机端app之将TensorF
  3. 关于android生成debug和release版
  4. Android的页面管理控件ViewFlipper
  5. Android之app作为服务器解决跨域问题
  6. Android分享---调用系统自带的分享功能
  7. Contentprovider的基本用法
  8. Android原生编解码接口 MediaCodec 之—
  9. Android(安卓)Template(模板) 编写(一)
  10. Android自检工具