序列化與反序列化
「序列化」是將對象轉換為可以存儲或傳輸的格式的過程。在計算機科學中,對象通常是指內存中的數據結構,如數組、列表、字典等。通過序列化,可以將這些對象轉換為字節流或文本格式,以便在不同的系統之間進行傳輸或存儲。序列化后的數據可以被傳輸到遠程系統,或者存儲在磁盤上,以便在需要時進行讀取和恢復。序列化的逆過程稱為反序列化,即將序列化后的數據重新轉換為原始對象的過程。
「反序列化」是將序列化后的數據恢復為原始對象的過程。在編程中,我們經常需要將對象序列化為字節流或者其他形式的數據,以便在網絡傳輸或者持久化存儲中使用。而反序列化則是將這些序列化后的數據重新轉換為原始對象。
在不同的編程語言中,反序列化的實現方式可能會有所不同。一般來說,反序列化的過程包括以下幾個步驟:
- 讀取序列化后的數據:從文件、網絡傳輸等地方讀取序列化后的數據。
- 解析數據:根據序列化的格式,解析數據并還原為原始的對象結構。
- 創建對象:根據解析得到的數據,創建對應的對象實例。
- 恢復對象狀態:將解析得到的數據賦值給對象的屬性,恢復對象的狀態。
反序列化的過程可以用以下偽代碼表示:
data = 讀取序列化后的數據
object = 解析數據(data)
在實際應用中,反序列化的方式和具體實現會根據編程語言和序列化庫的不同而有所差異。不同的序列化格式有不同的特點和適用場景,開發者可以根據具體需求選擇合適的序列化方式。
Android數據對象序列化的用途
Android數據對象序列化的主要用途是將對象轉換為字節流的形式,以便在網絡傳輸、持久化存儲或進程間通信中使用。具體的用途包括:
- 網絡傳輸:在Android開發中,我們經常需要將對象通過網絡傳輸給其他設備或服務器。通過序列化,我們可以將對象轉換為字節流,然后通過網絡發送給目標設備或服務器,目標設備或服務器再將字節流反序列化為對象進行處理。
- 持久化存儲:Android應用程序通常需要將數據保存在本地存儲中,以便在應用程序關閉后仍然可以訪問。通過序列化,我們可以將對象轉換為字節流,并將其保存在本地文件或數據庫中。當應用程序再次啟動時,我們可以將字節流反序列化為對象,以便恢復之前保存的數據。
- 進程間通信:在Android中,不同的組件(如Activity、Service、BroadcastReceiver等)可能運行在不同的進程中。通過序列化,我們可以將對象轉換為字節流,并通過進程間通信機制(如Binder)將字節流傳遞給其他進程,其他進程再將字節流反序列化為對象進行處理。
序列化提供了一種方便的方式來在不同的場景中傳輸和存儲對象數據。它在網絡傳輸、持久化存儲和進程間通信等方面都有廣泛的應用。
Android實現對象序列化的方式
在Android中,常用的實現對象序列化有以下幾種方式:
(1) 實現Serializable接口:在需要序列化的類中實現Serializable接口,該接口沒有任何方法,只是作為一個標記接口。然后使用ObjectOutputStream將對象寫入輸出流,使用ObjectInputStream從輸入流中讀取對象。示例代碼如下:
public class MyClass implements Serializable {
// 類的成員變量和方法
public static void mAIn(String[] args) {
// 序列化對象
MyClass obj = new MyClass();
try {
FileOutputStream fileOut = new FileOutputStream("object.ser");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(obj);
out.close();
fileOut.close();
System.out.println("對象已序列化");
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化對象
MyClass newObj = null;
try {
FileInputStream fileIn = new FileInputStream("object.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
newObj = (MyClass) in.readObject();
in.close();
fileIn.close();
System.out.println("對象已反序列化");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
(2) 實現Parcelable接口:Parcelable接口是Android特有的接口,相比Serializable接口,它更高效。在需要序列化的類中實現Parcelable接口,并實現相關方法。然后使用Parcel對象將對象寫入Parcel,使用Parcel對象從Parcel中讀取對象。示例代碼如下:
public class MyClass implements Parcelable {
// 類的成員變量和方法
protected MyClass(Parcel in) {
// 從Parcel中讀取數據并賦值給成員變量
}
public static final Creator<MyClass> CREATOR = new Creator<MyClass>() {
@Override
public MyClass createFromParcel(Parcel in) {
return new MyClass(in);
}
@Override
public MyClass[] newArray(int size) {
return new MyClass[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// 將成員變量寫入Parcel
}
}
(3) 使用Gson庫:Gson是google提供的一個用于在JAVA對象和JSON數據之間進行序列化和反序列化的庫。可以使用Gson將對象轉換為JSON字符串,然后再將JSON字符串轉換為對象。示例代碼如下:
public class MyClass {
// 類的成員變量和方法
public static void main(String[] args) {
// 序列化對象
MyClass obj = new MyClass();
Gson gson = new Gson();
String json = gson.toJson(obj);
System.out.println("對象已序列化為JSON字符串:" + json);
// 反序列化對象
MyClass newObj = gson.fromJson(json, MyClass.class);
System.out.println("JSON字符串已反序列化為對象");
}
}
序列化原理
「Serializable」是Java中的一個接口,用于實現對象的序列化和反序列化。序列化是指將對象轉換為字節流的過程,而反序列化則是將字節流轉換為對象的過程。
Serializable接口沒有任何方法,它只是一個標記接口,用于告訴Java虛擬機,該類可以被序列化。要實現序列化,只需要讓類實現Serializable接口即可。
在序列化過程中,Java虛擬機會將對象的狀態轉換為字節序列,然后可以將字節序列保存到文件、數據庫或通過網絡傳輸。反序列化過程則是將字節序列重新轉換為對象的狀態。
在序列化過程中,Java虛擬機會對對象的各個字段進行序列化。對于基本類型和引用類型,Java虛擬機會自動進行序列化。對于自定義類型,需要實現Serializable接口,并且保證該類型的所有成員變量也是可序列化的。
在反序列化過程中,Java虛擬機會根據字節序列重新創建對象,并將字節序列中的數據賦值給對象的各個字段。
需要注意的是,序列化和反序列化的過程中,對象的構造函數不會被調用。因此,在反序列化過程中,如果需要進行一些初始化操作,可以使用特殊的方法readObject()來實現。
總結起來,Serializable接口提供了一種簡單的方式來實現對象的序列化和反序列化。通過實現Serializable接口,可以將對象轉換為字節序列,以便在不同的環境中進行傳輸和存儲。
「Parcelable」是Android中用于實現對象序列化的接口。它的原理是將對象的數據按照一定的格式進行打包和解包,以便在不同的組件之間傳輸或存儲。
具體實現步驟如下:
- 實現Parcelable接口:在需要序列化的類中實現Parcelable接口,并實現其中的方法,包括describeContents()和writeToParcel(Parcel dest, int flags)。
- describeContents()方法:該方法返回一個標志位,用于描述Parcelable對象特殊對象的類型。一般情況下,返回0即可。
- writeToParcel(Parcel dest, int flags)方法:該方法將對象的數據寫入Parcel對象中。在該方法中,需要將對象的各個字段按照一定的順序寫入Parcel對象中,以便在解包時按照相同的順序讀取。
- 實現Parcelable.Creator接口:在需要序列化的類中實現Parcelable.Creator接口,并實現其中的方法,包括createFromParcel(Parcel source)和newArray(int size)。
- createFromParcel(Parcel source)方法:該方法從Parcel對象中讀取數據,并創建出Parcelable對象。在該方法中,需要按照寫入Parcel對象時的順序讀取數據,并將其賦值給相應的字段。
- newArray(int size)方法:該方法返回一個指定大小的Parcelable數組。
通過實現Parcelable接口,可以將對象的數據打包成一個Parcel對象,然后可以通過Intent傳遞給其他組件,或者通過Bundle存儲到本地。在接收端,可以通過讀取Parcel對象的數據,重新構建出原始的對象。
總結起來,Parcelable的原理就是將對象的數據按照一定的格式進行打包和解包,以實現對象的序列化和反序列化。這種方式相對于Java中的Serializable接口,更加高效和靈活。
Serializable/Parcelable對比
Serializable和Parcelable都是用于實現對象的序列化和反序列化的接口,但在實現方式和性能方面有所不同。
(1) Serializable:
- Serializable是Java提供的默認序列化機制,通過實現Serializable接口,可以將對象轉換為字節流,以便在網絡傳輸或保存到文件中。
- Serializable使用反射機制,將對象的狀態保存到字節流中,然后再從字節流中恢復對象的狀態。這種方式相對簡單,但效率較低。
- Serializable的缺點是序列化和反序列化的過程需要大量的I/O操作,對性能要求較高的場景下可能會影響程序的執行效率。
(2) Parcelable:
- Parcelable是Android提供的專門用于Android平臺的序列化機制,通過實現Parcelable接口,可以將對象轉換為字節流,以便在Activity之間傳遞。
- Parcelable使用了更加高效的序列化方式,將對象的狀態拆分為多個字段,分別寫入和讀取字節流。這種方式相對復雜,但效率較高。
- Parcelable的優點是序列化和反序列化的過程更加高效,對性能要求較高的場景下可以提升程序的執行效率。
Serializable適用于簡單的序列化場景,而Parcelable適用于對性能要求較高的Android平臺。在選擇使用Serializable還是Parcelable時,需要根據具體的需求和性能要求進行權衡。
數據來自parcelable-vs-serializable,實驗結果對比Parcelable的效率比Serializable快10倍以上。
總結
對比 |
Serializable |
Parcelable |
所屬API |
Java API |
Android SDK API |
特點 |
序列化和反序列化會經過大量的I/O操作,產生大量的臨時變量引起GC,且反序列化時需要反射 |
基于內存拷貝實現的封裝和解封(marshalled& unmarshalled),序列化基于Native層實現 |
開銷 |
相對高 |
相對低 |
效率 |
相對低 |
相對高 |
適用場景 |
簡單序列化 |
Android |
在使用「Serializable」進行對象的序列化時,有一些注意點需要注意:
- 類的定義:被序列化的類需要實現Serializable接口,這是Java提供的一個標記接口,用于表示該類可以被序列化。如果一個類沒有實現Serializable接口,那么在進行序列化時會拋出NotSerializableException異常。
- 成員變量的序列化:被序列化的類的所有成員變量都會被序列化,包括私有成員變量。但是,如果某個成員變量不希望被序列化,可以使用transient關鍵字進行修飾,被修飾的成員變量在序列化過程中會被忽略。
- 對象引用的序列化:如果一個類中包含其他對象的引用,那么在序列化時,被引用的對象也會被序列化。但是,如果被引用的對象沒有實現Serializable接口,那么在序列化時會拋出NotSerializableException異常。為了解決這個問題,可以將被引用的對象設置為transient,或者讓被引用的對象也實現Serializable接口。
- 序列化版本號:在進行對象的序列化時,會為每個被序列化的類自動生成一個序列化版本號。這個版本號用于在反序列化時判斷序列化的類和反序列化的類是否兼容。如果序列化的類和反序列化的類的版本號不一致,會拋出InvalidClassException異常。為了避免這個問題,可以顯式地為類指定一個固定的序列化版本號,可以使用serialVersionUID關鍵字進行指定。
- 序列化的安全性:在進行對象的序列化時,需要注意序列化的安全性。因為序列化的數據可以被反序列化成對象,如果序列化的數據被篡改,可能會導致安全漏洞。為了增強序列化的安全性,可以使用加密算法對序列化的數據進行加密,或者對序列化的類進行簽名驗證。
在使用「Parcelable」進行序列化時,有幾個注意點需要注意:
- 實現Parcelable接口:要使一個類可序列化,需要讓該類實現Parcelable接口,并實現其中的方法。這些方法包括writeToParcel()和createFromParcel()等。
- 內部類的序列化:如果要序列化的類中包含內部類,需要確保內部類也實現了Parcelable接口,并在外部類的writeToParcel()和createFromParcel()方法中對內部類進行序列化和反序列化。
- 序列化順序:在writeToParcel()方法中,需要按照成員變量的順序將數據寫入Parcel對象。在createFromParcel()方法中,需要按照寫入的順序讀取數據。
- 序列化和反序列化的一致性:在序列化和反序列化過程中,需要確保寫入和讀取的數據類型一致。例如,如果在writeToParcel()方法中寫入了一個整數,那么在createFromParcel()方法中讀取時也需要使用相同的方法讀取整數。
- 版本控制:如果在序列化的類中進行了修改,需要注意版本控制。可以通過給類添加一個版本號來實現版本控制,以便在反序列化時能夠正確處理不同版本的數據。
使用Parcelable進行序列化時,需要確保實現了Parcelable接口,并注意序列化順序、內部類的序列化、數據類型的一致性和版本控制等問題。