序列化作用
- 將對象持久化到存儲設備上
- 讓對象能夠在網絡中傳輸
- 讓對象能夠在IPC(進程間通信)中進行傳輸,作爲進程間通信的數據載體
序列化方式
在Android中要想實現序列化有兩種方式,分別是Serializable和Parcelable,其中Serializable是java提供的一個序列化接口,Parcelable是Android中提供的新的序列化接口。
Serializable
Serializable是一個空接口,要想通過Serializable實現序列化其實非常簡單,只需要實現這個接口並定義一個serialVersionUID即可(serialVersionUID並不是必須要定義,這個在下文中說明)。
這裏定義一個user類實現Serializable接口,如下所示。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int age;
}
要想實現對象的序列化,我們需要藉助Java IO流中的ObjectOutputStream和ObjectInputStream來完成對象的序列化和發序列化。
具體實現如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class Test {
public static void main(String[] args) {
write();
// read();
}
/**
* 序列化
*/
public static void write(){
try {
File file = new File("src/user.txt");
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos);
User user = new User("jack", 20);
System.out.println(user);
oos.writeObject(user);
oos.flush();
oos.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 反序列化
*/
public static void read(){
try {
File file = new File("src/user.txt");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
User user = (User) ois.readObject();
System.out.println(user);
ois.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
上述的代碼就是通過Serializable實現序列化的完整過程。
serialVersionUID的作用
在上文中我們曾提到在序列化時可以不用定義serialVersionUID,如果不定義serialVersionUID這對於對象的序列化是沒有任何影響的,但是會對對象的反序列化過程造成影響。
通常情況下,只有當序列化的數據的serialVersionUID和當前類的serialVersionUID相同時,數據纔會被反序列化。這裏我們可以通過上面的demo驗證這個問題,首先將User類中的serialVersionUID設置成1L,然後對該對象進行序列化,然後將serialVersionUID修改成2L,然後進行反序列化。發現此時程序會拋出異常:
java.io.InvalidClassException: User; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
這裏也特別提出,靜態變量和被transient關鍵字修飾的字段不會參與序列化。
Parcelable
Parcelable是Android提供的一個序列化接口,通過Parcelable實現序列化有一下幾個步驟:
- 實現Parcelable接口
- 重寫writeToParcel方法,該方法主要用於將對象的序列化數據存儲到Parcel對象中。Parcel封裝了一系列的write和read方法用於序列化和反序列化。
- 重寫describeContents方法,該方法默認返回0即可。(當存在文件描述符時返回1)
- 實例化靜態內部對象CREATOR實現接口Parcelable.Creator。用於反序列化。
具體用法如下:
package com.zhangke.test;
import android.os.Parcel;
import android.os.Parcelable;
/**
* Created by zhangke on 2017/2/8.
*/
public class User implements Parcelable {
private String username;
private int age;
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(username);
dest.writeInt(age);
}
public User(Parcel parcel) {
username = parcel.readString();
age = parcel.readInt();
}
}
兩種方式的比較
Serializable實現序列化簡單快捷,只需要實現一個接口即可。但是,通過該方式實現序列化及反序列化需要大量的IO操作,這樣造成的開銷較大;同時,通過Serializable底層要藉助反射來完成序列化,這種方式會導致序列化的過程較慢並在序列化的過程中創建許多的臨時對象,容易觸發垃圾回收。
Parcelable實現序列化優點是效率極高,缺點是實現細節比較複雜。
Parcelable主要用於對象的內存序列化上,雖然也能夠通過Parcelable將對象序列化到存儲設備中或將數據序列化後進行網絡傳輸,但是這個過程建議使用Seriaizalbe更加方便。