Android 序列化與反序列化

一、概述

對(duì)象序列化的意思就是將對(duì)象的狀態(tài)轉(zhuǎn)換成字節(jié)流,以后可以通過(guò)這些值在生成相同狀態(tài)的對(duì)象,對(duì)象序列化就是對(duì)象持久化的一種實(shí)現(xiàn)方法,它是將對(duì)象的屬性和方法轉(zhuǎn)化為一種序列化的形式用于存儲(chǔ)和傳輸,反序列化就是根據(jù)這些保存的信息重建對(duì)象的過(guò)程。

總之:

  • 序列化就是將對(duì)象轉(zhuǎn)化為字節(jié)流。
  • 反序列化就是將字節(jié)流轉(zhuǎn)換為對(duì)象。

二、序列化的應(yīng)用場(chǎng)景

1、永久性保存對(duì)象,保存對(duì)象的字節(jié)序列到本地文件中;

2、通過(guò)序列化對(duì)象在網(wǎng)絡(luò)中傳遞對(duì)象;

3、通過(guò)序列化在進(jìn)程間傳遞對(duì)象;

三、Serializable接口(java 自帶)

只需要讓需要序列化的對(duì)象實(shí)現(xiàn)Serializable,并提供一個(gè)序列化版本id(serialVersionUID)即可。剩下的系統(tǒng)會(huì)幫我們自動(dòng)的序列化。

下面是代碼示例,通過(guò)Serializable接口來(lái)實(shí)現(xiàn)深克隆。

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;
    }

    /**
     * 通過(guò)序列化完成克隆
     * @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;
    }

}

注意點(diǎn):使用 static、transient 修飾的屬性不會(huì)被序列化。

如果需要定制序列化,則需要自己實(shí)現(xiàn) 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
    
    }

在反序列化時(shí),如果類的定義發(fā)生了變化,版本號(hào)就會(huì)變化,與流中的版本號(hào)就會(huì)不匹配,反序列化就會(huì)拋出異常,類型為java.io.InvalidClassException。通常情況下,我們希望自定義這個(gè)版本號(hào),而非讓Java自動(dòng)生成,一方面是為了更好的控制,另一方面是為了性能,因?yàn)镴ava自動(dòng)生成的性能比較低。

四、Parcelable接口(Android專用)

實(shí)現(xiàn)Parcelable步驟

1、implements Parcelable

2、重寫writeToParcel方法,將你的對(duì)象序列化為一個(gè)Parcel對(duì)象,即:將類的數(shù)據(jù)寫入外部提供的Parcel中,打包需要傳遞的數(shù)據(jù)到Parcel容器保存,以便從 Parcel容器獲取數(shù)據(jù)

3、重寫describeContents方法,內(nèi)容接口描述,默認(rèn)返回0就可以

4、實(shí)例化靜態(tài)內(nèi)部對(duì)象CREATOR實(shí)現(xiàn)接口Parcelable.Creator

public static final Parcelable.Creator CREATOR

注:其中public static final一個(gè)都不能少,內(nèi)部對(duì)象CREATOR的名稱也不能改變,必須全部大寫。需重寫本接口中的兩個(gè)方法:createFromParcel(Parcel in) 實(shí)現(xiàn)從Parcel容器中讀取傳遞數(shù)據(jù)值,封裝成Parcelable對(duì)象返回邏輯層,newArray(int size) 創(chuàng)建一個(gè)類型為T,長(zhǎng)度為size的數(shù)組,僅一句話即可(return new T[size]),供外部類反序列化本類數(shù)組使用。

簡(jiǎn)而言之:通過(guò)writeToParcel將你的對(duì)象映射成Parcel對(duì)象,再通過(guò)createFromParcel將Parcel對(duì)象映射成你的對(duì)象。也可以將Parcel看成是一個(gè)流,通過(guò)writeToParcel把對(duì)象寫到流里面,在通過(guò)createFromParcel從流里讀取對(duì)象,只不過(guò)這個(gè)過(guò)程需要你來(lái)實(shí)現(xiàn),因此寫的順序和讀的順序必須一致。

示例代碼:

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];
        }
    };
}

補(bǔ)充:可以通過(guò)Android Studio插件一鍵生成Parcelable代碼(Android Parcelable code generator插件)。寫完實(shí)體類后在Generate界面(Alt + Insert鍵)選擇Parcelable一鍵生成。

五、Parcelable和Serializable的區(qū)別

1、作用
Serializable的作用是為了保存對(duì)象的屬性到本地文件、數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)流、RMI(Remote Method Invocation)以方便數(shù)據(jù)傳輸,當(dāng)然這種傳輸可以是程序內(nèi)的也可以是兩個(gè)程序間的。而Android的Parcelable的設(shè)計(jì)初衷是因?yàn)镾erializable效率過(guò)慢,為了在程序內(nèi)不同組件間以及不同Android程序間高效的傳輸數(shù)據(jù)而設(shè)計(jì),這些數(shù)據(jù)僅在內(nèi)存中存在,Parcelable是通過(guò)IBinder通信的消息的載體。

2、效率及選擇
Serializable是Java中的序列化接口,其使用起來(lái)簡(jiǎn)單但開(kāi)銷較大(因?yàn)镾erializable在序列化過(guò)程中使 用了反射機(jī)制,故而會(huì)產(chǎn)生大量的臨時(shí)變量,從而導(dǎo)致頻繁的GC),并且在讀寫數(shù)據(jù)過(guò)程中,它是通 過(guò)IO流的形式將數(shù)據(jù)寫入到硬盤或者傳輸?shù)骄W(wǎng)絡(luò)上。Parcelable的性能比Serializable好,在內(nèi)存開(kāi)銷方面較小,所以在內(nèi)存間數(shù)據(jù)傳輸時(shí)推薦使用Parcelable,如activity間傳輸數(shù)據(jù)。而Serializable可將數(shù)據(jù)持久化方便保存,Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性,使用相對(duì)復(fù)雜,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時(shí)選擇Serializable。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、前言 對(duì)于Android開(kāi)發(fā)者來(lái)說(shuō),IPC機(jī)制肯定不陌生,而作為Android的進(jìn)階也必須掌握IPC機(jī)制。所謂...
    丶藍(lán)天白云夢(mèng)閱讀 3,878評(píng)論 14 18
  • 什么是序列化/反序列化? 簡(jiǎn)單來(lái)說(shuō)就是將對(duì)象轉(zhuǎn)換為可以傳輸?shù)亩M(jìn)制流(二進(jìn)制序列)的過(guò)程,這樣我們就可以通過(guò)序列化...
    有腹肌的豌豆Z閱讀 2,118評(píng)論 0 0
  • 由于進(jìn)行通信肯定要涉及數(shù)據(jù)的處理,所以我們需要先了解兩個(gè)基礎(chǔ)的概念,序列化和反序列化。 定義 序列化:將對(duì)象轉(zhuǎn)化為...
    Eric_feng閱讀 1,245評(píng)論 0 1
  • 概念 正式的:序列化:把對(duì)象轉(zhuǎn)換為字節(jié)序列的過(guò)程稱為對(duì)象的序列化。反序列化:把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱為對(duì)象的反...
    dlihasa閱讀 728評(píng)論 0 1
  • What? 何為序列化與反序列化?序列化:將對(duì)象轉(zhuǎn)化為二進(jìn)制序列的過(guò)程反序列化:將二進(jìn)制序列恢復(fù)為原始對(duì)象的過(guò)程 ...
    LilacZiyun閱讀 3,132評(píng)論 0 15

友情鏈接更多精彩內(nèi)容