Serializable和Parcelable

Serializable和Parcelable

在日常的应用开发中,我们可能需要让某些对象离开内存空间,存储到物理磁盘,以便长期保存,同时也能减少对内存的压力,而在需要时再将其从磁盘读取到内存,比如将某个特定的对象保存到文件中,隔一段时间后再把它读取到内存中使用,那么该对象就需要实现序列化操作,在Java中可以使用Serializable接口实现对象的序列化,而在android中既可以使用Serializable接口实现对象序列化也可以使用Parcelable接口实现对象序列化,但是在内存操作时更倾向于实现Parcelable接口,这样会使用传输效率更高效。

  • 序列化:
    将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。

  • 反序列化
    就是读取序列化后保存在存储区的序列化信息或反序列化对象的状态,重新创建该对象。

序列化和反序列化通常是并存的,他们的关系就像进行加密和解密操作一样。前者需要相同的序列化方式,后者需要知道秘钥。

  • 实现序列化的必要条件
    一个对象要实现序列化操作,该类就必须实现了Serializable接口或者Parcelable接口,其中Serializable接口是在java中的序列化抽象类,而Parcelable接口则是android中特有的序列化接口,在某些情况下,Parcelable接口实现的序列化更为高效,关于它们的实现案例我们后续会分析,这里只要清楚知道实现序列化操作时必须实现Serializable接口或者Parcelable接口之一即可。

Serializable是java提供的一个序列化接口,它是一个空接口,专门为对象提供标准的序列化和反序列化操作,使用Serializable来实现序列化相当简单,只需要在类的声明中指定一个类似下面的标示即可自动实现默认的序列化过程(serialVersionUID可自动生成也可自己写,如0L,1L,2L…)。

import java.io.Serializable;

public class User implements Serializable {
     private static final long serialVersionUID = -4454266436543306544L;
        public int userId;
        public String userName;
        public User(int userId, String userName) {
            this.userId = userId;
            this.userName = userName;
        }

}
  • 关于 serialVersionUID
    想让一个对象实现序列化,只需要这个类实现Serializable接口并声明一个serialVersionUID即可,实际上,甚至这个serialVersionUID也不是必需的,我们不声明这个serialVersionUID同样可以实现序列化,但是这将会对反序列化过程产生影响,具体会产生什么影响呢?

    • 为什么需要serialVersionUID呢?
      因为静态成员变量属于类不属于对象,不会参与序列化过程,使用transient关键字标记的成员变量也不参与序列化过程。 (PS:关键字transient,这里简单说明一下,Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的)

    • 序列化过程
      具体过程是这样的:序列化操作的时候系统会把当前类的serialVersionUID写入到序列化文件中,当反序列化时系统会去检测文件中的serialVersionUID,判断它是否与当前类的serialVersionUID一致,如果一致就说明序列化类的版本与当前类版本是一样的,可以反序列化成功,否则失败。

    • serialVersionUID 对反序列化的影响
      如果不手动指定serialVersionUID的值,反序列化时当前类有所改变,比如增加或者删除了某些成员变量,那么系统就会重新计算当前类的hash值并且把它赋值给serialVersionUID,这个时候当前类的serialVersionUID就和序列化的数据中的serialVersionUID不一致,于是反序列化失败。所以我们手动指定serialVersionUID的值能很大程度上避免了反序列化失败。

代码实现

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 Main {

    public static void main(String[] args) {
        // 序列化过程
        User user = new User(1, "Tom");
        ObjectOutputStream out;
        try {
            out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
            out.writeObject(user);
            out.close();
            // 反序列化过程
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
            try {
                User newuser = (User) in.readObject();
                System.out.println(newuser.userId);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
  • Parcelable
    Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如通过Intent在activity间传输数据,而Parcelable的缺点就使用起来比较麻烦。

Android studio自动生成的序列化代码:

package com.gechao.customview01;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by Gechao on 2017/5/4.
 * ToDo
 */

public class User implements Parcelable {
    private String userId;
    private String name;
    private String pass;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.userId);
        dest.writeString(this.name);
        dest.writeString(this.pass);
    }

    public User() {
    }

    protected User(Parcel in) {
        this.userId = in.readString();
        this.name = in.readString();
        this.pass = in.readString();
    }

    public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() {
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
}

按常规方法写一个实体类,然后

前提是安装了插件,并且打开了相关设置,会自动生成 id

  • Parcelable 与 Serializable 区别

    • 两者的实现差异
        Serializable的实现,只需要实现Serializable接口即可。这只是给对象打了一个标记(UID),系统会自动将其序列化。而Parcelabel的实现,不仅需要实现Parcelabel接口,还需要在类中添加一个静态成员变量CREATOR,这个变量需要实现 Parcelable.Creator 接口,并实现读写的抽象方法。

    • 两者的设计初衷
        Serializable的设计初衷是为了序列化对象到本地文件、数据库、网络流、RMI以便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是由于Serializable效率过低,消耗大,而android中数据传递主要是在内存环境中(内存属于android中的稀有资源),因此Parcelable的出现为了满足数据在内存中低开销而且高效地传递问题。

    • 两者效率选择
        Parcelable的性能比Serializable好,在内存开销方面较小,所以Android应用程序在内存间数据传输时推荐使用Parcelable,如activity间传输数据和AIDL数据传递,而Serializable将数据持久化的操作方便,因此在将对象序列化到存储设置中或将对象序列化后通过网络传输时建议选择Serializable(Parcelable也是可以,只不过实现和操作过程过于麻烦并且为了防止android版本不同而导致Parcelable可能不同的情况,因此在序列化到存储设备或者网络传输方面还是尽量选择Serializable接口)。

    • 两者需要注意的共同点
        无论是Parcelable还是Serializable,执行反序列操作后的对象都是新创建的,与原来的对象并不相同,只不过内容一样罢了。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值