說明
序列化是一種用來處理對象流的機制。
所謂對象流:就是将對象的内容進行流化。可以對流化後的對象進行讀寫操作,也可将流化後的對象傳輸于網絡之間。
序列化:把Java對象轉換為位元組序列的過程。
反序列化:把位元組序列恢複為Java對象的過程。
序列化目的
序列化是為了解決在對對象流進行讀寫操作時所引發的問題。
序列化的實作:将需要被序列化的類實作Serializable接口(标記接口),該接口沒有需要實作的方法,implements Serializable隻是為了标注該對象是可被序列化的,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(對象流)對象;接着,使用ObjectOutputStream對象的writeObject(Object obj)方法就可以将參數為obj的對象寫出(即儲存其狀态),要恢複的話則用輸入流。
什麼時候使用序列化
一:對象序列化可以實作分布式對象。
主要應用例如:RMI(即遠端調用Remote Method Invocation)要利用對象序列化運作遠端主機上的服務,就像在本地機上運作對象時一樣。
二:java對象序列化不僅保留一個對象的資料,而且遞歸儲存對象引用的每個對象的資料。
可以将整個對象層次寫入位元組流中,可以儲存在檔案中或在網絡連接配接上傳遞。利用對象序列化可以進行對象的"深複制",即複制對象本身及引用的對象本身。序列化一個對象可能得到整個對象序列。
三:序列化可以将記憶體中的類寫入檔案或資料庫中。
比如:将某個類序列化後存為檔案,下次讀取時隻需将檔案中的資料反序列化就可以将原先的類還原到記憶體中。也可以将類序列化為流資料進行傳輸。總的來說就是将一個已經執行個體化的類轉成檔案存儲,下次需要執行個體化的時候隻要反序列化即可将類執行個體化到記憶體中并保留序列化時類中的所有變量和狀态。
四: 對象、檔案、資料,有許多不同的格式,很難統一傳輸和儲存。
序列化以後就都是位元組流了,無論原來是什麼東西,都能變成一樣的東西,就可以進行通用的格式傳輸或儲存,傳輸結束以後,要再次使用,就進行反序列化還原,這樣對象還是對象,檔案還是檔案
因為JAVA中要将對象序列化為 流的形式 進行傳輸。
對象的序列化就是為了資料傳輸,在你的代碼的裡是對象格式,而在傳輸的時候不可能還保持這對象的樣子。
當兩個程序在進行遠端通信時,彼此可以發送各種類型的資料。無論是何種類型的資料,都會以二進制序列的形式在網絡上傳送。發送方需要把這個Java對象轉換為位元組序列,才能在網絡上傳送;接收方則需要把位元組序列再恢複為Java對象。
對象的序列化主要有兩種用途:
1)把對象的位元組序列永久地儲存到硬碟上,通常存放在一個檔案中。
2)在網絡上傳送對象的位元組序列。
所謂的Serializable,就是java提供的通用資料儲存和讀取的接口。至于從什麼地方讀出來和儲存到哪裡去都被隐藏在函數參數的背後了。這樣子,任何類型隻要實作了Serializable接口,就可以被儲存到檔案中,或者作為資料流通過網絡發送到别的地方。也可以用管道來傳輸到系統的其他程式中。這樣子極大的簡化了類的設計。隻要設計一個儲存一個讀取功能就能解決上面說得所有問題。
java的"對象序列化"能讓你将一個實作了Serializable接口的對象轉換成一組byte,這樣日後要用這個對象時候,你就能把這些byte資料恢複出來,并據此重新建構那個對象了。
工作流當中流程變量的幾種資料類型:string、integer、short、long、double、boolean、date、binary、serializable,這就是為什麼要将javabean實作序列化的原因,因為你将對象設定到流程變量中必須要實作序列化,否則會在設定流程變量的時候報錯找不到該類型。
java對象序列化機制就是把記憶體中的Java對象(User之類的JavaBean)轉換成二進制流。java對象序列化後可以很友善的存儲或者在網絡中傳輸。
Java的序列化機制是通過運作時判斷類的序列化ID(serialVersionUID)來判定版本的一緻性。
在反序列化時,java虛拟機會通過二進制流中的serialVersionUID與本地的對應的實體類進行比較,如果相同就認為是一緻的,可以進行反序列化,正确獲得資訊,否則抛出序列化版本不一緻的異常。
是以涉及到資料傳輸或者存儲的類,嚴格意義上來說都要加上序列化ID,這也是一種良好的程式設計習慣
兩者都是安卓常用到的序列化對象,Parcelable要手動寫構造函數和writeToParcel,不過現在as都是自動生成的,Serializable是聲明一下接口就行了。Parcelable比Serializable性能強,Serializable在使用是會産生大量臨時變量,增加GC回收頻率。但是Serializable的資料是以IO流在磁盤,而Parcelable是寫在記憶體,是以Parcelable無法将資料更好的持久化。
Serializable是屬于Java自帶的,本質是使用了反射。序列化的過程比較慢,這種機制在序列化的時候會建立很多臨時的對象,比引起頻繁的GC。Parcelable 是屬于 Android 專用。不過不同于Serializable,Parcelable實作的原理是将一個完整的對象進行分解。而分解後的每一部分都是Intent所支援的資料類型。 如果在記憶體中使用建議Parcelable。持久化操作建議Serializable
另外 Parcelable 的讀寫順序必須一緻, 流化的資料通過Intent進行傳遞,Intent 傳值有大小限制;
Intent 中的 Bundle 是使用 Binder 機制進行資料傳送的, 資料會寫到核心空間, Binder 緩沖區域;
Binder 的緩沖區是有大小限制的, 有些 ROM 是 1M, 有些 ROM 是 2M;
這個限制定義在 frameworks/native/libs/binder/processState.cpp 類中, 如果超過這個限制, 系統就會報錯;
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) ;
因為 Binder 本身就是為了程序間頻繁-靈活的通信所設計的, 并不是為了拷貝大量資料;
Parcelable的使用
1.對象類實作Parcelable接口
2.添加相應的屬性參數
3.完成set 、get、構造器的建立
4.之後查找爆紅的地方 按Alt+Enter(Android studio中)進行錯誤處理 完成上述之後會有如下結構呈現出來,序列化完成後,bean對象就可以進行資料傳遞了,更多的注釋内容放到代碼中了
public class Animals implements Parcelable {
private String species; // 種類
private int id; // 編号
private static final String TAG = "Tag";
public Animals(String species, int id) {
Log.i(TAG, "Animals: 構造器");
this.species = species;
this.id = id;
}
/**
* 這裡讀取順序一定要與writeToParcel(Parcel dest, int flags)的寫入順序一緻
* 其指派順序會根據寫入順序進行,順序一旦調整 寫入的資料就會不對應 進而發生錯誤
* AS工具會提示對象的讀取指派操作,一般ALT+Enter快捷完成 自動生成就可以了
*
* @param in
*/
protected Animals(Parcel in) {
Log.i(TAG, "Parcel為參數指派 ");
species = in.readString();
id = in.readInt();
}
public static final Creator<Animals> CREATOR = new Creator<Animals>() {
@Override
public Animals createFromParcel(Parcel in) {
// 傳回Parcelable類的新執行個體。同Parcel的readXXX()方法來完成反序列化的過程。
Log.i(TAG, "createFromParcel: 傳回新執行個體");
return new Animals(in);
}
@Override
public Animals[] newArray(int size) {
//建立Parcelable類的新數組
Log.i(TAG, "newArray: 傳回新數組");
return new Animals[size];
}
};
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
/**
* describeContents 翻譯過來就是内容描述,其實預設傳回0即可
*
* @return
*/
@Override
public int describeContents() {
Log.i(TAG, "describeContents: 内容描述");
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
//将目前的對象寫入序列化結構中,dest表示需要寫入序列化的對象。
// flags有兩種 0和1(PARCELABLE_WRITE_RETURN_VALUE),幾乎所有的情況下都是0.
Log.i(TAG, "writeToParcel: 目前的對象寫入序列化結構中");
dest.writeString(species);
dest.writeInt(id);
}
}
5.通過列印log 我們看看再傳遞這個bean對象的過程中,執行個體parcelable接口的對象都幹了什麼

通過列印log也知道了所謂序列化都幹了些啥,雖然相比Serializable的使用來講麻煩了許多,但是還是要根據不同的使用場景來選擇使用哪種序列化方式