美文网首页
Android 序列化与反序列化

Android 序列化与反序列化

作者: mumuxi_ | 来源:发表于2022-04-30 00:38 被阅读0次

    一、概述

    对象序列化的意思就是将对象的状态转换成字节流,以后可以通过这些值在生成相同状态的对象,对象序列化就是对象持久化的一种实现方法,它是将对象的属性和方法转化为一种序列化的形式用于存储和传输,反序列化就是根据这些保存的信息重建对象的过程。

    总之:

    • 序列化就是将对象转化为字节流。
    • 反序列化就是将字节流转换为对象。

    二、序列化的应用场景

    1、永久性保存对象,保存对象的字节序列到本地文件中;

    2、通过序列化对象在网络中传递对象;

    3、通过序列化在进程间传递对象;

    三、Serializable接口(java 自带)

    只需要让需要序列化的对象实现Serializable,并提供一个序列化版本id(serialVersionUID)即可。剩下的系统会帮我们自动的序列化。

    下面是代码示例,通过Serializable接口来实现深克隆。

    import java.io.Serializable;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class TestSeriazableClone {
    
        public static void main(String[] args) {
            SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米饭"));
            SPerson p2 = (SPerson)p1.cloneBySerializable();
            p2.setName("其不答");
            p2.getFood().setName("面条");
            System.out.println(p1);
            System.out.println(p2);
        }
    
    }
    
    class SPerson implements Cloneable,Serializable{
    
        private static final long serialVersionUID = -7710144514831611031L;
    
        private int pid;
    
        private String name;
    
        private SFood food;
    
        public SPerson(int pid, String name, SFood food) {
            this.pid = pid;
            this.name = name;
            this.food = food;
            System.out.println("Person constructor call");
        }
    
        public int getPid() {
            return pid;
        }
    
        public void setPid(int pid) {
            this.pid = pid;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        /**
         * 通过序列化完成克隆
         * @return
         */
        public Object cloneBySerializable() {
            Object obj = null;
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                obj = ois.readObject();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return obj;
        }
    
        @Override
        public String toString() {
            return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
        }
    
        public SFood getFood() {
            return food;
        }
    
        public void setFood(SFood food) {
            this.food = food;
        }
    
    }
    
    class SFood implements Serializable {
    
        private static final long serialVersionUID = -3443815804346831432L;
    
        private String name;
    
        public SFood(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    }
    

    注意点:使用 static、transient 修饰的属性不会被序列化。

    如果需要定制序列化,则需要自己实现 writeObject 和readObject 方法。

    private void writeObject(ObjectOutputStream out) throws IOException {
            //invoke default serialization method
         
        }
    
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            //invoke default serialization method
        
        }
    

    在反序列化时,如果类的定义发生了变化,版本号就会变化,与流中的版本号就会不匹配,反序列化就会抛出异常,类型为java.io.InvalidClassException。通常情况下,我们希望自定义这个版本号,而非让Java自动生成,一方面是为了更好的控制,另一方面是为了性能,因为Java自动生成的性能比较低。

    四、Parcelable接口(Android专用)

    实现Parcelable步骤

    1、implements Parcelable

    2、重写writeToParcel方法,将你的对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从 Parcel容器获取数据

    3、重写describeContents方法,内容接口描述,默认返回0就可以

    4、实例化静态内部对象CREATOR实现接口Parcelable.Creator

    public static final Parcelable.Creator CREATOR

    注:其中public static final一个都不能少,内部对象CREATOR的名称也不能改变,必须全部大写。需重写本接口中的两个方法:createFromParcel(Parcel in) 实现从Parcel容器中读取传递数据值,封装成Parcelable对象返回逻辑层,newArray(int size) 创建一个类型为T,长度为size的数组,仅一句话即可(return new T[size]),供外部类反序列化本类数组使用。

    简而言之:通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射成你的对象。也可以将Parcel看成是一个流,通过writeToParcel把对象写到流里面,在通过createFromParcel从流里读取对象,只不过这个过程需要你来实现,因此写的顺序和读的顺序必须一致。

    示例代码:

    import android.os.Parcel;
    import android.os.Parcelable;
    
    /**
     * @author mumuxi
     * @version 2022/4/29
     */
    public class TestParcelable implements Parcelable {
    
        private int age;
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        private String name;
    
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(this.age);
            dest.writeString(this.name);
        }
    
        public void readFromParcel(Parcel source) {
            this.age = source.readInt();
            this.name = source.readString();
        }
    
        public TestParcelable() {
        }
    
        protected TestParcelable(Parcel in) {
            this.age = in.readInt();
            this.name = in.readString();
        }
    
        public static final Parcelable.Creator<TestParcelable> CREATOR =
                new Parcelable.Creator<TestParcelable>() {
            @Override
            public TestParcelable createFromParcel(Parcel source) {
                return new TestParcelable(source);
            }
    
            @Override
            public TestParcelable[] newArray(int size) {
                return new TestParcelable[size];
            }
        };
    }
    

    补充:可以通过Android Studio插件一键生成Parcelable代码(Android Parcelable code generator插件)。写完实体类后在Generate界面(Alt + Insert键)选择Parcelable一键生成。

    五、Parcelable和Serializable的区别

    1、作用
    Serializable的作用是为了保存对象的属性到本地文件、数据库、网络流、RMI(Remote Method Invocation)以方便数据传输,当然这种传输可以是程序内的也可以是两个程序间的。而Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。

    2、效率及选择
    Serializable是Java中的序列化接口,其使用起来简单但开销较大(因为Serializable在序列化过程中使 用了反射机制,故而会产生大量的临时变量,从而导致频繁的GC),并且在读写数据过程中,它是通 过IO流的形式将数据写入到硬盘或者传输到网络上。Parcelable的性能比Serializable好,在内存开销方面较小,所以在内存间数据传输时推荐使用Parcelable,如activity间传输数据。而Serializable可将数据持久化方便保存,Parcelable不能很好的保证数据的持续性,使用相对复杂,所以在需要保存或网络传输数据时选择Serializable。

    相关文章

      网友评论

          本文标题:Android 序列化与反序列化

          本文链接:https://www.haomeiwen.com/subject/lolsyrtx.html