序列化與反序列化

序列化

將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制串的過程。

反序列化

將在序列化過程中所生成的二進(jìn)制串轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對象的過程

序列化/反序列化的目的

簡單的概括

  • 序列化:主要用于網(wǎng)絡(luò)傳輸,數(shù)據(jù)持久化,一般序列化也稱為編碼(Encode)
  • 反序列化:主要用于從網(wǎng)絡(luò),磁盤上讀取字節(jié)數(shù)組還原成原始對象,一般反序列化也稱為解碼 (Decode)

具體的講:

  • 永久的保存對象數(shù)據(jù)(將對象數(shù)據(jù)保存在文件當(dāng)中,或者是磁盤中);
  • 通過序列化操作將對象數(shù)據(jù)在網(wǎng)絡(luò)上進(jìn)行傳輸(由于網(wǎng)絡(luò)傳輸是以字節(jié)流的方式對數(shù)據(jù)進(jìn)行傳輸?shù)模虼诵蛄谢哪康氖菍ο髷?shù)據(jù)轉(zhuǎn)換成字節(jié)流的形式) ;
  • 將對象數(shù)據(jù)在進(jìn)程之間進(jìn)行傳遞(Activity之間傳遞對象數(shù)據(jù)時,需要在當(dāng)前的Activity中對對象數(shù)據(jù)進(jìn)行序列化操作。在另一個Activity中需要進(jìn)行反序列化操作講數(shù)據(jù)取出);
  • Java平臺允許我們在內(nèi)存中創(chuàng)建可復(fù)用的Java對象,但一般情況下,只有當(dāng)JVM處于運行時,這些對象才可能存在,即這些對象的生命周期不會比JVM的生命周期更長(即每個對象都在JVM中) 。但在現(xiàn)實應(yīng)用中,就可能要停止JVM運行,但有要保存某些指定的對象,并在將來重新讀取被保存的對象。這是Java對象序列化就能夠?qū)崿F(xiàn)該功能。(可選擇入數(shù)據(jù)庫、或文件的形式保存);
  • 序列化對象的時候只是針對變量進(jìn)行序列化,不針對方法進(jìn)行序列化;
  • 在Intent之間,基本的數(shù)據(jù)類型直接進(jìn)行相關(guān)傳遞即可,但是一旦數(shù)據(jù)類型比較復(fù)雜的時候,就需要進(jìn) 行序列化操作了。

常見的序列化和反序列化協(xié)議

XML&SOAP

XML 是一種常用的序列化和反序列化協(xié)議,具有跨機(jī)器,跨語言等優(yōu)點,SOAP(Simple Object Access protocol) 是一種被廣泛應(yīng)用的,基于 XML 為序列化和反序列化協(xié)議的結(jié)構(gòu)化消息傳遞協(xié)議。

JSON(Javascript Object Notation)

JSON 起源于弱類型語言 Javascript, 它的產(chǎn)生來自于一種稱之為"Associative array"的概念,其本質(zhì)是 就是采用"Attribute-value"的方式來描述對象。實際上在 Javascript 和 PHP 等弱類型語言中,類的描 述方式就是 Associative array。JSON 的如下優(yōu)點,使得它快速成為最廣泛使用的序列化協(xié)議之一。

  • 這種 Associative array 格式非常符合工程師對對象的理解;

  • 它保持了 XML 的人眼可讀(Human-readable)的優(yōu)點;

  • 相對于 XML 而言,序列化后的數(shù)據(jù)更加簡潔。來自于的以下鏈接的研究表明:XML 所產(chǎn)生序列化之后文件的大小接近 JSON 的兩倍;

  • 它具備 Javascript 的先天性支持,所以被廣泛應(yīng)用于 Web browser 的應(yīng)用常景中,是 Ajax 的事實標(biāo)準(zhǔn)協(xié)議;

  • 與 XML 相比,其協(xié)議比較簡單,解析速度比較快;

  • 松散的 Associative array 使得其具有良好的可擴(kuò)展性和兼容性。

Protobuf

Protobuf 具備了優(yōu)秀的序列化協(xié)議的所需的眾多典型特征。

  • 標(biāo)準(zhǔn)的 IDL 和 IDL 編譯器,這使得其對工程師非常友好。
  • 序列化數(shù)據(jù)非常簡潔,緊湊,與 XML 相比,其序列化之后的數(shù)據(jù)量約為 1/3 到 1/10。
  • 解析速度非常快,比對應(yīng)的 XML 快約 20-100 倍。
  • 提供了非常友好的動態(tài)庫,使用非常簡介,反序列化只需要一行代碼。

Serializable接口

是 Java 提供的序列化接口,它是一個空接口:

public interface Serializable {
}

Serializable 用來標(biāo)識當(dāng)前類可以被 ObjectOutputStream 序列化,以及被 ObjectInputStream 反序列 化。

Serializable入門

public class Student implements Serializable { 
 private static final long serialVersionUID = -2100492893943893602L;//serialVersionUID唯一標(biāo)識了一個可序列化的類
 private String name;
 private String sax;
 private Integer age;

 private List<Course> courses;//Course也需要實現(xiàn)Serializable接口 

 //用transient關(guān)鍵字標(biāo)記的成員變量不參與序列化
 //(在被反序列化后,transient變量的值被設(shè)為初始值,如int型的是 0,對象型的是null)
 private transient Date createTime;

 //靜態(tài)成員變量屬于類不屬于對象,所以不會參與序列化
 //(對象序列化保存的是對象的“狀態(tài)”,也 就是它的成員變量,因此序列化不會關(guān)注靜態(tài)變量)
 private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
    public Student() {
        System.out.println("Student: empty");
    }

    public Student(String name, String sax, Integer age) {
        System.out.println("Student: " + name + " " + sax + " " + age);
        this.name = name;
        this.sax = sax;
        this.age = age;
        courses = new ArrayList<>();
        createTime = new Date();、
    }
 }

 //Course也需要實現(xiàn)Serializable接口
 public class Course implements Serializable {

    private static final long serialVersionUID = 667279791530738499L;
    private String name;
    private float score;
 }
Serializable 有以下幾個特點:
  • 可序列化類中,未實現(xiàn) Serializable 的屬性狀態(tài)無法被序列化/反序列化;
  • 也就是說,反序列化一個類的過程中,它的非可序列化的屬性將會調(diào)用無參構(gòu)造函數(shù)重新創(chuàng)建 ;
  • 因此這個屬性的無參構(gòu)造函數(shù)必須可以訪問,否者運行時會報錯;
  • 一個實現(xiàn)序列化的類,它的子類也是可序列化的。
serialVersionUID與兼容性
  • serialVersionUID的作用

    serialVersionUID 用來表明類的不同版本間的兼容性。如果你修改了此類, 要修改此值。否則以前用老版本的類序列化的類恢復(fù)時會報錯: InvalidClassException

  • 設(shè)置方式

    在JDK中,可以利用JDK的bin目錄下的serialver.exe工具產(chǎn)生這個serialVersionUID,對于 Test.class,執(zhí)行命令:serialver Test

  • 兼容性問題

    為了在反序列化時,確保類版本的兼容性,最好在每個要序列化的類中加入 private static final long serialVersionUID這個屬性,具體數(shù)值自己定義。

    這樣,即使某個類在與之對應(yīng)的對象已經(jīng)序列化出去后做了修改,該對象依然可以被正確反序列化。否則,如果不顯式定義該屬性,這個屬性值將由JVM根據(jù)類的相關(guān)信息計算,而修改后的類的計算結(jié)果與修改前的類的計算結(jié)果往往不同,從而造成對象的反序列化因為類版本不兼容而失敗。

    不顯式定義這個屬性值的另一個壞處是,不利于程序在不同的JVM之間的移植。因為不同的編譯器 實現(xiàn)該屬性值的計算策略可能不同,從而造成雖然類沒有改變,但是因為JVM不同,出現(xiàn)因類版本不兼容而無法正確反序列化的現(xiàn)象出現(xiàn)。

因此 JVM 規(guī)范強烈建議我們手動聲明一個版本號,這個數(shù)字可以是隨機(jī)的,只要固定不變就可以。同時最好是 private 和 final 的,盡量保證不變。

Externalizable接口

public interface Externalizable extends java.io.Serializable {
    void writeExternal(ObjectOutput out) throws IOException;
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

簡單使用

public class Course1 implements Externalizable {
    private static final long serialVersionUID = 667279791530738499L;
    private String name;
    private float score;
    ...
   @Override
    public void writeExternal(ObjectOutput objectOutput) throws IOException{
        System.out.println("writeExternal");
        objectOutput.writeObject(name);
        objectOutput.writeFloat(score);
    }

    @Override
    public void readExternal(ObjectInput objectInput) throws IOException,
        ClassNotFoundException {
        System.out.println("readExternal");
        name = (String)objectInput.readObject();
        score = objectInput.readFloat();
   } 
   ...
    public static void main(String... args) throws Exception {
        Course1 course = new Course1("英語", 12f); 
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out); 
        oos.writeObject(course);
        course.setScore(78f);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bs));
        Course1 course1 = (Course1) ois.readObject();
        System.out.println("course1: " + course1);
}

readObject/writeObject原理分析

以 oos.writeObject(obj) 為例分析

  1. ObjectOutputStream的構(gòu)造函數(shù)設(shè)置enableOverride = false

    public ObjectOutputStream(OutputStream var1) throws IOException {
        this.verifySubclass();
        this.bout = new ObjectOutputStream.BlockDataOutputStream(var1);
        this.handles = new ObjectOutputStream.HandleTable(10, 3.0F);
        this.subs = new ObjectOutputStream.ReplaceTable(10, 3.0F);
        this.enableOverride = false;     //有參構(gòu)造函數(shù),設(shè)置enableOverride = false
        this.writeStreamHeader();
        this.bout.setBlockDataMode(true);
        if (extendedDebugInfo) {
            this.debugInfoStack = new ObjectOutputStream.DebugTraceInfoStack();
        } else {
            this.debugInfoStack = null;
        }
    }
    
  2. 所以writeObject方法執(zhí)行的是writeObject0(obj, false);

    public final void writeObject(Object var1) throws IOException {
        if (this.enableOverride) {
            this.writeObjectOverride(var1);
        } else {                          
            try {
                this.writeObject0(var1, false);    //enableOverride=false,走這里
            } catch (IOException var3) {
                if (this.depth == 0) {
                    this.writeFatalException(var3);
                }
    
                throw var3;
            }
        }
    }
    
  3. 在writeObject0方法中,代碼非常多,看重點

    private void writeObject0(Object var1, boolean var2) throws IOException {
    ...
     if (var1 instanceof Enum) {
        this.writeEnum((Enum)var1, var7, var2);
     } else {
        if (!(var1 instanceof Serializable)) {
           if (extendedDebugInfo) {
                throw new NotSerializableException(var6.getName() + "\n" + this.debugInfoStack.toString());
           }
    
           //如果沒有實現(xiàn)Serializable接口,會報NotSerializableException
           throw new NotSerializableException(var6.getName());
        }
    
         //實現(xiàn)Serializable接口,走這里
         this.writeOrdinaryObject(var1, var7, var2);//實現(xiàn)了Serializable
       }
    
       return;
       ...
    }
    
  4. 在writeOrdinaryObject(obj, desc, unshared)方法中

    private void writeOrdinaryObject(Object var1, ObjectStreamClass var2, boolean var3) throws IOException {
        if (extendedDebugInfo) {
            this.debugInfoStack.push((this.depth == 1 ? "root " : "") + "object (class \"" + var1.getClass().getName() + "\", " + var1.toString() + ")");
        }
    
        try {
            var2.checkSerialize();
            this.bout.writeByte(115);
            this.writeClassDesc(var2, false);
            this.handles.assign(var3 ? null : var1);
            if (var2.isExternalizable() && !var2.isProxy()) {
                //如果對象實現(xiàn)了Externalizable接口,那么執(zhí)行 writeExternalData((Externalizable) obj)方法
                this.writeExternalData((Externalizable)var1);
            } else {
                //如果對象實現(xiàn)的是Serializable接口,那么執(zhí)行的是writeSerialData(obj, desc)
                this.writeSerialData(var1, var2);
            }
        } finally {
            if (extendedDebugInfo) {
                this.debugInfoStack.pop();
            }
        }
    }
    
  5. 如果對象實現(xiàn)的是Serializable接口,走的是writeSerialData(var1, var2)方法,最終寫序列化的地方

    private void writeSerialData(Object var1, ObjectStreamClass var2) throws IOException {
        ClassDataSlot[] var3 = var2.getClassDataLayout();
    
        for(int var4 = 0; var4 < var3.length; ++var4) {
            ObjectStreamClass var5 = var3[var4].desc;
            
            //如果writeObjectMethod != null(目標(biāo)類中定義了私有的writeObject方法),那么將調(diào)用目標(biāo)類中的writeObject方法
            if (var5.hasWriteObjectMethod()) {
                ObjectOutputStream.PutFieldImpl var6 = this.curPut;
                this.curPut = null;
                SerialCallbackContext var7 = this.curContext;
                if (extendedDebugInfo) {
                    this.debugInfoStack.push("custom writeObject data (class \"" + var5.getName() + "\")");
                }
    
                try {
                    this.curContext = new SerialCallbackContext(var1, var5);
                    this.bout.setBlockDataMode(true);
                    var5.invokeWriteObject(var1, this);//定義了writeObject方法,去執(zhí)行invokeWriteObject(var1, this)
                    this.bout.setBlockDataMode(false);
                    this.bout.writeByte(120);
                } finally {
                    this.curContext.setUsed();
                    this.curContext = var7;
                    if (extendedDebugInfo) {
                        this.debugInfoStack.pop();
                    }
    
                }
    
                this.curPut = var6;
            } else {
                //如果如果writeObjectMethod == null, 那么將調(diào)用默認(rèn)的 defaultWriteFields方法來讀取目標(biāo)類中的屬性
                this.defaultWriteFields(var1, var5);
            }
        }
    
    }
    
  6. 調(diào)用了ObjectStreamClass的hasWriteObjectMethod()來判斷是否定義了私有的writeObject方法。

    boolean hasWriteObjectMethod() {
        this.requireInitialized();
        return this.writeObjectMethod != null;
    }
    
  7. 那writeObjectMethod變量是在哪里定義的呢?在ObjectStreamClass構(gòu)造函數(shù)中。

    private ObjectStreamClass(final Class<?> var1) {
     ...
        if (ObjectStreamClass.this.externalizable) {
            ObjectStreamClass.this.cons = ObjectStreamClass.getExternalizableConstructor(var1);
        } else {
            ObjectStreamClass.this.cons = ObjectStreamClass.getSerializableConstructor(var1);
            
            //在序列化(反序列化)的時候, ObjectOutputStream(ObjectInputStream)
            //會尋找目標(biāo)類中的私有的writeObject(readObject)方法,
            //賦值給變量writeObjectMethod(readObjectMethod)
            ObjectStreamClass.this.writeObjectMethod = ObjectStreamClass.getPrivateMethod(var1, "writeObject", new Class[]{ObjectOutputStream.class}, Void.TYPE);
            
            ObjectStreamClass.this.readObjectMethod = ObjectStreamClass.getPrivateMethod(var1, "readObject", new Class[]{ObjectInputStream.class}, Void.TYPE);
            ObjectStreamClass.this.readObjectNoDataMethod = ObjectStreamClass.getPrivateMethod(var1, "readObjectNoData", (Class[])null, Void.TYPE);
            ObjectStreamClass.this.hasWriteObjectData = ObjectStreamClass.this.writeObjectMethod != null;
         }
     ...
    }
    

Serializable需要注意的坑

  • 多引用寫入

    在默認(rèn)情況下, 對于一個實例的多個引用,為了節(jié)省空間,只會寫入一次,后面會追加幾個字節(jié)代表某 個實例的引用。

  • 子類實現(xiàn)序列化,父類不實現(xiàn)序列化/ 對象引用

    在readObject時拋出java.io.NotSerializableException異常。

  • 類的演化

    反序列化目標(biāo)類多一個字段,并不會報錯,只是實賦成了默認(rèn)值。
    還有,如果寫入的多一個字段,讀出的少一個字段,也是不會報錯的。

  • 枚舉類型

    事實上序列化Enum對象時,并不會保存元素的值,只會保存元素的name。這樣,在不依賴元素值的 前提下,ENUM對象如何更改都會保持兼容性。

Parcelable接口

Parcelable是Android為我們提供的序列化的接口,Parcelable相對于Serializable的使用相對復(fù)雜一些,但 Parcelable的效率相對Serializable也高很多,這一直是Google工程師引以為傲的,有時間的可以看一下 Parcelable和Serializable的效率對比,Parcelable vs Serializable 號稱快10倍的效率。

Parcelable是Android SDK提供的,它是基于內(nèi)存的,由于內(nèi)存讀寫速度高于硬盤,因此Android中的跨進(jìn)程對象的傳遞一般使用Parcelable。

Parcelable與Serializable的性能比較

Serializable性能分析

Serializable是Java中的序列化接口,其使用起來簡單但開銷較大(因為Serializable在序列化過程中使 用了反射機(jī)制,故而會產(chǎn)生大量的臨時變量,從而導(dǎo)致頻繁的GC),并且在讀寫數(shù)據(jù)過程中,它是通 過IO流的形式將數(shù)據(jù)寫入到硬盤或者傳輸?shù)骄W(wǎng)絡(luò)上。

Parcelable性能分析

Parcelable則是以IBinder作為信息載體,在內(nèi)存上開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞時,推薦使用Parcelable,而Parcelable對數(shù)據(jù)進(jìn)行持久化或者網(wǎng)絡(luò)傳輸時操作復(fù)雜,一般這個時候推薦使用 Serializable。

注意點:Parcelable安全漏洞

性能比較總結(jié)描述

Parcelable的性能要強于Serializable的原因:

  • 在內(nèi)存的使用中,前者在性能方面要強于后者;

  • 后者在序列化操作的時候會產(chǎn)生大量的臨時變量(原因是使用了反射機(jī)制),從而導(dǎo)致GC的頻繁調(diào)用,因此在性能上會稍微遜色;

  • Parcelable是以Ibinder作為信息載體的。在內(nèi)存上的開銷比較小,因此在內(nèi)存之間進(jìn)行數(shù)據(jù)傳遞的時候,Android推薦使用Parcelable。既然是內(nèi)存方面比價有優(yōu)勢,那么自然就要優(yōu)先選擇;

  • 在讀寫數(shù)據(jù)的時候,Parcelable是在內(nèi)存中直接進(jìn)行讀寫,而Serializable是通過使用IO流的形式將數(shù)據(jù)讀寫入在硬盤上;

    但是雖然Parcelable的性能要強于Serializable,但是仍然有特殊的情況需要使用Serializable,而不去使用Parcelable。因為Parcelable無法將數(shù)據(jù)進(jìn)行持久化,因此在將數(shù)據(jù)保存在磁盤的時候,仍然需要使用后者,因為前者無法很好的將數(shù)據(jù)進(jìn)行持久化(原因是在不同的Android版本當(dāng)中,Parcelable可能會不同,因此數(shù)據(jù)的持久化方面仍然是使用Serializable)。

性能測試方法分析

  • 通過將一個對象放到一個bundle里面,然后調(diào)用Bundle#writeToParcel(Parcel, int)方法來模擬;

    傳遞對象給一個activity的過程,然后再把這個對象取出來。

  • 在一個循環(huán)里面運行1000 次。

  • 兩種方法分別運行10次來減少內(nèi)存整理,cpu被其他應(yīng)用占用等情況的干擾。

  • 參與測試的對象就是上面的相關(guān)代碼

  • 在多種Android軟硬件環(huán)境上進(jìn)行測試

兩種如何選擇

  • 在使用內(nèi)存方面,Parcelable比Serializable性能高,所以推薦使用Parcelable;
  • Serializable在序列化的時候會產(chǎn)生大量的臨時變量,從而引起頻繁的GC;
  • Parcelable不能使用在要將數(shù)據(jù)存儲在磁盤上的情況,因為Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性,在外界有變化的情況下,建議使用Serializable。

Parcelable與Serializable的區(qū)別

Parcelable Serializable
直接在內(nèi)存操作,效率高,性能好 通過IO對硬盤操作,速度較慢
一般不能超過1M,修改內(nèi)核也只能4M 大小不受限制
大量使用反射,產(chǎn)生內(nèi)存碎片

SQLite與SharedPreferences

  • SQLite主要用于存儲復(fù)雜的關(guān)系型數(shù)據(jù),Android支持原生支持SQLite數(shù)據(jù)庫相關(guān)操作 (SQLiteOpenHelper),不過由于原生API接口并不友好,所以產(chǎn)生了不少封裝了SQLite的ORM框架。

  • SharedPreferences是Android平臺上提供的一個輕量級存儲API,一般用于存儲常用的配置信息,其本質(zhì)是一個鍵值對存儲,支持常用的數(shù)據(jù)類型如boolean、float、int、long以及String的存儲和讀取。

相關(guān)面試題

  1. ①什么是 serialVersionUID?作用是什么?如果你不定義這個, 會發(fā)生什么?

    ②假設(shè)你有一個類,它序列化并存儲在持久性中,然后修改了該類以添加新字段。如果對已序列化的對象進(jìn)行反序列化,會發(fā)生什么情況?

    serialVersionUID 是一個 private static final long 型 ID,當(dāng)它被印在對象上時,它通常是對象的哈希碼,你可以使用 serialver 這個 JDK 工具來查看序列化對象的 serialVersionUID。 SerialVerionUID 用于對象的版本控制。也可以在類文件中指定 serialVersionUID。不指定 serialVersionUID的后果是,當(dāng)你添加或修改類中的任何字段時,,則已序列化類將無法恢復(fù),因為為新類和舊序列化對象生成的 serialVersionUID 將有所不同。Java 序列化過程依賴于正確的序列化對象恢復(fù)狀態(tài)的,并在序列化對象序列版本不匹配的情況下引發(fā)java.io.InvalidClassException 無效類異常。

  2. 序列化時,你希望某些成員不要序列化?你如何實現(xiàn)它?

    有時候也會變著形式問,比如問什么是瞬態(tài) trasient 變量,瞬態(tài)和靜態(tài)變量會不會得到序列化等,所以,如果你不希望任何字段是對象的狀態(tài)的一部分,然后聲明它靜態(tài)或瞬態(tài)根據(jù)你的需要,這樣就不會是在 Java 序列化過程中被包含在內(nèi)。

  3. 如果類中的一個成員未實現(xiàn)可序列化接口,會發(fā)生什么情況?

    如果嘗試序列化實現(xiàn)可序列化的類的對象,但該對象包含對不可序列化類的引用,則在運行時將引發(fā)不可序列化異常 NotSerializableException

  4. 如果類是可序列化的,但其超類不是,則反序列化后從超級類繼承的實例變量的狀態(tài)如何?

    Java 序列化過程僅在對象層次都是可序列化結(jié)構(gòu)中繼續(xù),即實現(xiàn) Java 中的可序列化接口,并且從超級類繼承的實例變量的值將通過調(diào)用構(gòu)造函數(shù)初始化,在反序列化過程中不可序列化的超級類。

  5. ①是否可以自定義序列化過程,或者是否可以覆蓋 Java 中的默認(rèn)序列化過程?

    ②假設(shè)新類的超級類實現(xiàn)可序列化接口,如何避免新類被序列化?

    對于序列化一個對象需調(diào)用 ObjectOutputStream.writeObject(saveThisObject),并用 ObjectInputStream.readObject() 讀取對象,但 Java 虛擬機(jī)為你提供的還有一件事,是定義這兩個方法。如果在類中定義這兩種方法,則 JVM 將調(diào)用這兩種方法,而不是應(yīng)用默認(rèn)序列化機(jī)制。 你可以在此處通過執(zhí)行任何類型的預(yù)處理或后處理任務(wù)來自定義對象序列化和反序列化的行為。

  6. 在 Java 中的序列化和反序列化過程中使用哪些方法?

    考察你是否熟悉 readObject() 的用法、writeObject()、readExternal() 和 writeExternal()。 Java 序列化由java.io.ObjectOutputStream類完成。該類是一個篩選器流,它封裝在較低級別的字節(jié)流中,以處理序列化機(jī)制。要通過序列化機(jī)制存儲任何對象,我們調(diào)用 ObjectOutputStream.writeObject(savethisobject),并反序列化該對象,我們稱之為 ObjectInputStream.readObject()方法。調(diào)用以 writeObject() 方法在 java 中觸發(fā)序列化過程。 關(guān)于 readObject() 方法,需要注意的一點很重要一點是,它用于從持久性讀取字節(jié),并從這些字節(jié)創(chuàng)建對象,并返回一個對象,該對象需要類型強制轉(zhuǎn)換為正確的類型。

  7. 反序列化后的對象,需要調(diào)用構(gòu)造函數(shù)重新構(gòu)造嗎

    不會,因為是從二進(jìn)制直接解析出來的。適用的是 Object 進(jìn)行接收再強轉(zhuǎn),因此不是原來的那個對象。

  8. 序列化與持久化的關(guān)系和區(qū)別是什么?

    序列化是為了進(jìn)程間數(shù)據(jù)交互而設(shè)計的,持久化是為了把數(shù)據(jù)存儲下來而設(shè)計的。

  9. 序列前的對象與序列化后的對象是什么關(guān)系?是("=="還是equal?是淺復(fù)制還是 深復(fù)制?)//枚舉

    是一個深拷貝, 前后對象的引用地址不同

  10. Android里面為什么要設(shè)計出Bundle而不是直接用Map結(jié)構(gòu)?

    Bundle內(nèi)部是由ArrayMap實現(xiàn)的,ArrayMap的內(nèi)部實現(xiàn)是兩個數(shù)組,一個int數(shù)組是存儲對象數(shù)據(jù)對應(yīng)下標(biāo),一個對象數(shù)組保存key和value,內(nèi)部使用二分法對key進(jìn)行排序,所以在添加、刪除、查找數(shù)據(jù)的時候,都會使用二分法查找,只適合于小數(shù)據(jù)量操作,如果在數(shù)據(jù)量比較大的情況下,那么它的性能將退化。而HashMap內(nèi)部則是數(shù)組+鏈表結(jié)構(gòu),所以在數(shù)據(jù)量較少的時候,HashMap的Entry Array比ArrayMap占用更多的內(nèi)存。因為使用Bundle的場景大多數(shù)為小數(shù)據(jù)量,我沒見過在兩個Activity之間傳遞10個以上數(shù)據(jù)的場景,所以相比之下,在這種情況下使用 ArrayMap保存數(shù)據(jù),在操作速度和內(nèi)存占用上都具有優(yōu)勢,因此使用Bundle來傳遞數(shù)據(jù),可以保 證更快的速度和更少的內(nèi)存占用。

    另外一個原因,則是在Android中如果使用Intent來攜帶數(shù)據(jù)的話,需要數(shù)據(jù)是基本類型或者是可序列化類型,HashMap使用Serializable進(jìn)行序列化,而Bundle則是使用Parcelable進(jìn)行序列化。 而在Android平臺中,更推薦使用Parcelable實現(xiàn)序列化,雖然寫法復(fù)雜,但是開銷更小,所以為了更加快速的進(jìn)行數(shù)據(jù)的序列化和反序列化,系統(tǒng)封裝了Bundle類,方便我們進(jìn)行數(shù)據(jù)的傳輸。

  11. Android中Intent/Bundle的通信原理及大小限制?

    Android 中的 bundle 實現(xiàn)了 parcelable 的序列化接口,目的是為了在進(jìn)程間進(jìn)行通訊。不同的進(jìn)程共享一片固定大小的內(nèi)存。parcelable 利用 parcel 對象的 read/write 方法,對需要傳遞的數(shù)據(jù)進(jìn)行內(nèi)存讀寫,因此這一塊共享內(nèi)存不能過大,在利用 bundle 進(jìn)行傳輸時,會初始化一個 BINDER_VM_SIZE 的大小 = 1 * 1024 * 1024 - 4096 * 2,即便通過 修改 Framework 的代碼,bundle 內(nèi)核的映射只有 4M,最大只能擴(kuò)展到 4M。

  12. 為何Intent不能直接在組件間傳遞對象而要通過序列化機(jī)制?

    因為 Activity 啟動過程是需要與 AMS 交互,AMS 與 UI 進(jìn)程是不同一個的,因此進(jìn)程間需要交互數(shù)據(jù),就必須序列化。
    更詳細(xì)的結(jié)識需要結(jié)合AMS。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,995評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容