Serializable和Parcelable的区别

说明

序列化是一种用来处理对象流的机制。

所谓对象流:就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。

序列化:把Java对象转换为字节序列的过程。

反序列化:把字节序列恢复为Java对象的过程。

 序列化目的

序列化是为了解决在对对象流进行读写操作时所引发的问题。

序列化的实现:将需要被序列化的类实现Serializable接口(标记接口),该接口没有需要实现的方法,implements Serializable只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象;接着,使用ObjectOutputStream对象的writeObject(Object obj)方法就可以将参数为obj的对象写出(即保存其状态),要恢复的话则用输入流。

什么时候使用序列化

一:对象序列化可以实现分布式对象。

主要应用例如:RMI(即远程调用Remote Method Invocation)要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。

二:java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。

可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的"深复制",即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。

三:序列化可以将内存中的类写入文件或数据库中。

比如:将某个类序列化后存为文件,下次读取时只需将文件中的数据反序列化就可以将原先的类还原到内存中。也可以将类序列化为流数据进行传输。总的来说就是将一个已经实例化的类转成文件存储,下次需要实例化的时候只要反序列化即可将类实例化到内存中并保留序列化时类中的所有变量和状态。

四: 对象、文件、数据,有许多不同的格式,很难统一传输和保存。

序列化以后就都是字节流了,无论原来是什么东西,都能变成一样的东西,就可以进行通用的格式传输或保存,传输结束以后,要再次使用,就进行反序列化还原,这样对象还是对象,文件还是文件

因为JAVA中要将对象序列化为 流的形式 进行传输。

对象的序列化就是为了数据传输,在你的代码的里是对象格式,而在传输的时候不可能还保持这对象的样子。

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

对象的序列化主要有两种用途:

1)把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。

2)在网络上传送对象的字节序列。

所谓的Serializable,就是java提供的通用数据保存和读取的接口。至于从什么地方读出来和保存到哪里去都被隐藏在函数参数的背后了。这样子,任何类型只要实现了Serializable接口,就可以被保存到文件中,或者作为数据流通过网络发送到别的地方。也可以用管道来传输到系统的其他程序中。这样子极大的简化了类的设计。只要设计一个保存一个读取功能就能解决上面说得所有问题。

java的"对象序列化"能让你将一个实现了Serializable接口的对象转换成一组byte,这样日后要用这个对象时候,你就能把这些byte数据恢复出来,并据此重新构建那个对象了。

工作流当中流程变量的几种数据类型:string、integer、short、long、double、boolean、date、binary、serializable,这就是为什么要将javabean实现序列化的原因,因为你将对象设置到流程变量中必须要实现序列化,否则会在设置流程变量的时候报错找不到该类型。

java对象序列化机制就是把内存中的Java对象(User之类的JavaBean)转换成二进制流。java对象序列化后可以很方便的存储或者在网络中传输。

Java的序列化机制是通过运行时判断类的序列化ID(serialVersionUID)来判定版本的一致性。

在反序列化时,java虚拟机会通过二进制流中的serialVersionUID与本地的对应的实体类进行比较,如果相同就认为是一致的,可以进行反序列化,正确获得信息,否则抛出序列化版本不一致的异常。

所以涉及到数据传输或者存储的类,严格意义上来说都要加上序列化ID,这也是一种良好的编程习惯

两者都是安卓常用到的序列化对象,Parcelable要手动写构造函数和writeToParcel,不过现在as都是自动生成的,Serializable是声明一下接口就行了。Parcelable比Serializable性能强,Serializable在使用是会产生大量临时变量,增加GC回收频率。但是Serializable的数据是以IO流在磁盘,而Parcelable是写在内存,所以Parcelable无法将数据更好的持久化。

Serializable是属于Java自带的,本质是使用了反射。序列化的过程比较慢,这种机制在序列化的时候会创建很多临时的对象,比引起频繁的GC。Parcelable 是属于 Android 专用。不过不同于Serializable,Parcelable实现的原理是将一个完整的对象进行分解。而分解后的每一部分都是Intent所支持的数据类型。 如果在内存中使用建议Parcelable。持久化操作建议Serializable

另外 Parcelable 的读写顺序必须一致, 流化的数据通过Intent进行传递,Intent 传值有大小限制;

Intent 中的 Bundle 是使用 Binder 机制进行数据传送的, 数据会写到内核空间, Binder 缓冲区域;

Binder 的缓冲区是有大小限制的, 有些 ROM 是 1M, 有些 ROM 是 2M;

这个限制定义在 frameworks/native/libs/binder/processState.cpp 类中, 如果超过这个限制, 系统就会报错;

#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) ;

因为 Binder 本身就是为了进程间频繁-灵活的通信所设计的, 并不是为了拷贝大量数据;

 

Parcelable的使用

1.对象类实现Parcelable接口

2.添加相应的属性参数

3.完成set 、get、构造器的创建

4.之后查找爆红的地方 按Alt+Enter(Android studio中)进行错误处理 完成上述之后会有如下结构呈现出来,序列化完成后,bean对象就可以进行数据传递了,更多的注释内容放到代码中了

public class Animals implements Parcelable {
    private String species; // 种类
    private int id;         // 编号
    private static final String TAG = "Tag";

    public Animals(String species, int id) {
        Log.i(TAG, "Animals: 构造器");
        this.species = species;
        this.id = id;
    }

    /**
     * 这里读取顺序一定要与writeToParcel(Parcel dest, int flags)的写入顺序一致
     * 其赋值顺序会根据写入顺序进行,顺序一旦调整 写入的数据就会不对应 从而发生错误
     * AS工具会提示对象的读取赋值操作,一般ALT+Enter快捷完成 自动生成就可以了
     *
     * @param in
     */
    protected Animals(Parcel in) {
        Log.i(TAG, "Parcel为参数赋值 ");
        species = in.readString();
        id = in.readInt();
    }

    public static final Creator<Animals> CREATOR = new Creator<Animals>() {
        @Override
        public Animals createFromParcel(Parcel in) {
            // 返回Parcelable类的新实例。同Parcel的readXXX()方法来完成反序列化的过程。
            Log.i(TAG, "createFromParcel: 返回新实例");
            return new Animals(in);
        }

        @Override
        public Animals[] newArray(int size) {
            //创建Parcelable类的新数组
            Log.i(TAG, "newArray: 返回新数组");
            return new Animals[size];
        }
    };

    public String getSpecies() {
        return species;
    }

    public void setSpecies(String species) {
        this.species = species;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    /**
     * describeContents 翻译过来就是内容描述,其实默认返回0即可
     *
     * @return
     */
    @Override
    public int describeContents() {
        Log.i(TAG, "describeContents: 内容描述");
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //将当前的对象写入序列化结构中,dest表示需要写入序列化的对象。
        // flags有两种 0和1(PARCELABLE_WRITE_RETURN_VALUE),几乎所有的情况下都是0.
        Log.i(TAG, "writeToParcel: 当前的对象写入序列化结构中");
        dest.writeString(species);
        dest.writeInt(id);
    }
}

 5.通过打印log 我们看看再传递这个bean对象的过程中,实例parcelable接口的对象都干了什么

通过打印log也知道了所谓序列化都干了些啥,虽然相比Serializable的使用来讲麻烦了许多,但是还是要根据不同的使用场景来选择使用哪种序列化方式

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值