1.什麼是序列化和反序列化
數據序列化就是將數據結構或者是對象轉換成我們可以存儲或者傳輸的數據格式的一個過程,在序列化的過程中,數據結構或者對象將其狀態信息寫入到臨時或者持久性的存儲區中,而在對應的反序列化過程中,則是生成的數據被還原成數據結構或對象的過程。一句話概括序列化就是將數據結構或對象轉換成二進制串的過程
,反序列化就是將在序列化過程中所生成的二進制串轉換成數據結構或者對象的過程
json的拼裝和解析也可以看作一種序列化和反序列化
2.爲什麼需要序列化
由於在系統底層,數據的傳輸形式是簡單的字節序列(二進制)形式傳遞,即在底層,系統不認識對象,只認識字節序列,而爲了達到進程通訊的目的,需要先將數據序列化,而序列化就是將對象轉化字節序列的過程。相反地,當字節序列被運到相應的進程的時候,進程爲了識別這些數據,就要將其反序列化,即把字節序列轉化爲對象
3.序列化的作用
爲數據傳遞提供基礎
4.序列化的是實現方式
a.Serializable接口
是 Java 提供的序列化接口
public interface Serializable { }
b.Externalizable
是實現了Serializable的一個子接口
public interface Externalizable extends Serializable {
void writeExternal(ObjectOutput var1) throws IOException;
void readExternal(ObjectInput var1) throws IOException, ClassNotFoundException;
}
和Serializable的區別就是通過writeExternal方法自定義需要序列化的變量,哪些是我們需要的,我們就自己把它序列化進去,不像Serializable,他是默認將所有可以序列化的變量都序列化了,可定製性不強;同理,通過readExternal讀取我們需要的參數。如下:
雖然類Man中定義了三個變量,但是我們指定了序列化只會對age和name進行,所以打印的情況會是下邊這樣,因爲沒有指定des的序列化,所以des打印結果是null
class Man implements Externalizable{
public int age;
public String name;
public String des;
public Man (){}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(age);
out.writeObject(name);
System.out.println("writeExternal");
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
age = in.readInt();
name = (String) in.readObject();
System.out.println("readExternal");
}
}
public static void main(String[] args) {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(CurPath+"/ren.txt"));
Man obj = new Man ();
obj.age = 1;
obj.des = "我是我";
obj.name = "名字的";
oos.writeObject(obj);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(CurPath+"/ren.txt"));
Man m = (Man ) ois.readObject();
System.out.println(m.age);
System.out.println(m.name);
System.out.println(m.des);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
打印結果:
1
名字的
null
c.Parcellable
Parcelable是Android提供的序列化的接口,Parcelable相對於Serializable的使用相對複雜一些,但Parcelable的效率相對Serializable高很多,號稱快10倍,
Parcelable是Android SDK提供的,它是基於內存的,由於內存讀寫速度高於硬盤,因此Android中的跨進程對象的傳遞一般使用Parcelable
Parcelable與Serializable的性能比較
1.Serializable性能分析
Serializable是Java中的序列化接口,其使用起來簡單但開銷較大(因爲Serializable在序列化過程中使用了反射機制,故而會產生大量的臨時變量,從而導致頻繁的GC),並且在讀寫數據過程中,它是通過IO流的形式將數據寫入到硬盤或者傳輸到網絡上。
2.Parcelable性能分析
Parcelable則是以IBinder作爲信息載體,在內存上開銷比較小,因此在內存之間進行數據傳遞時,推薦使用Parcelable,而Parcelable對數據進行持久化或者網絡傳輸時操作複雜,一般這個時候推薦使用Serializable。
雖然Parcelable的性能要強於Serializable,但是仍然有特殊的情況需要使用Serializable,而不去使用Parcelable,因爲Parcelable無法將數據進行持久化,因此在將數據保存在磁盤的時候,仍然需要使用後者,因爲前者無法很好的將數據進行持久化.(原因是在不同的Android版本當中,Parcelable可能會不同,因此數據的持久化方面仍然是使用Serializable)
相關面試題
1.Android裏面爲什麼要設計出Bundle而不是直接用Map結構
1.Bundle內部是由ArrayMap實現的,ArrayMap的內部實現是兩個數組,一個int數組是存儲對象數據對應下標,一個對象數組保存key和value,內部使用二分法對key進行排序,所以在添加、刪除、查找數據的時候,都會使用二分法查找,只適合於小數據量操作,如果在數據量比較大的情況下,那麼它的性能將退化。而HashMap內部則是數組+鏈表結構,所以在數據量較少的時候,HashMap的Entry Array比ArrayMap佔用更多的內存。因爲使用Bundle的場景大多數爲小數據量,我沒見過在兩個Activity之間傳遞10個以上數據的場景,所以相比之下,在這種情況下使用ArrayMap保存數據,在操作速度和內存佔用上都具有優勢,因此使用Bundle來傳遞數據,可以保證更快的速度和更少的內存佔用。
2.另外一個原因,則是在Android中如果使用Intent來攜帶數據的話,需要數據是基本類型或者是可序列化類型,HashMap使用Serializable進行序列化,而Bundle則是使用Parcelable進行序列化。而在Android平臺中,更推薦使用Parcelable實現序列化,雖然寫法複雜,但是開銷更小,所以爲了更加快速的進行數據的序列化和反序列化,系統封裝了Bundle類,方便我們進行數據的傳輸。
2.Android中Intent/Bundle的通信原理及大小限制
Intent 中的 Bundle 是使用 Binder 機制進行數據傳送的。能使用的 Binder 的緩衝區是有大小限制的(有些手機是 2 M),而一個進程默認有 16 個 Binder 線程,所以一個線程能佔用的緩衝區就更小了( 有人以前做過測試,大約一個線程可以佔用 128 KB)。所以當你看到 The Bindertransaction failed because it was too large 這類 TransactionTooLargeException 異常時,你應該知道怎麼解決了
3.爲何Intent不能直接在組件間傳遞對象而要通過序列化機制?
Intent在啓動其他組件時,會離開當前應用程序進程,進入ActivityManagerService進程(intent.prepareToLeaveProcess()),這也就意味着,Intent所攜帶的數據要能夠在不同進程間傳輸。首先我們知道,Android是基於Linux系統,不同進程之間的java對象是無法傳輸,所以我們此處要對對象進行序列化,從而實現對象在 應用程序進程 和 ActivityManagerService進程 之間
傳輸。而Parcel或者Serializable都可以將對象序列化,其中,Serializable使用方便,但性能不如Parcel容器,後者也是Android系統專門推出的用於進程間通信等的接口