一、概述
對(duì)象序列化的意思就是將對(duì)象的狀態(tài)轉(zhuǎn)換成字節(jié)流,以后可以通過(guò)這些值在生成相同狀態(tài)的對(duì)象,對(duì)象序列化就是對(duì)象持久化的一種實(shí)現(xiàn)方法,它是將對(duì)象的屬性和方法轉(zhuǎn)化為一種序列化的形式用于存儲(chǔ)和傳輸,反序列化就是根據(jù)這些保存的信息重建對(duì)象的過(guò)程。
總之:
- 序列化就是將對(duì)象轉(zhuǎn)化為字節(jié)流。
- 反序列化就是將字節(jié)流轉(zhuǎn)換為對(duì)象。
二、序列化的應(yīng)用場(chǎng)景
1、永久性保存對(duì)象,保存對(duì)象的字節(jié)序列到本地文件中;
2、通過(guò)序列化對(duì)象在網(wǎng)絡(luò)中傳遞對(duì)象;
3、通過(guò)序列化在進(jìn)程間傳遞對(duì)象;
三、Serializable接口(java 自帶)
只需要讓需要序列化的對(duì)象實(shí)現(xiàn)Serializable,并提供一個(gè)序列化版本id(serialVersionUID)即可。剩下的系統(tǒng)會(huì)幫我們自動(dòng)的序列化。
下面是代碼示例,通過(guò)Serializable接口來(lái)實(shí)現(xiàn)深克隆。
import java.io.Serializable;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class TestSeriazableClone {
public static void main(String[] args) {
SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米飯"));
SPerson p2 = (SPerson)p1.cloneBySerializable();
p2.setName("其不答");
p2.getFood().setName("面條");
System.out.println(p1);
System.out.println(p2);
}
}
class SPerson implements Cloneable,Serializable{
private static final long serialVersionUID = -7710144514831611031L;
private int pid;
private String name;
private SFood food;
public SPerson(int pid, String name, SFood food) {
this.pid = pid;
this.name = name;
this.food = food;
System.out.println("Person constructor call");
}
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 通過(guò)序列化完成克隆
* @return
*/
public Object cloneBySerializable() {
Object obj = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
obj = ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return obj;
}
@Override
public String toString() {
return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
}
public SFood getFood() {
return food;
}
public void setFood(SFood food) {
this.food = food;
}
}
class SFood implements Serializable {
private static final long serialVersionUID = -3443815804346831432L;
private String name;
public SFood(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
注意點(diǎn):使用 static、transient 修飾的屬性不會(huì)被序列化。
如果需要定制序列化,則需要自己實(shí)現(xiàn) writeObject 和readObject 方法。
private void writeObject(ObjectOutputStream out) throws IOException {
//invoke default serialization method
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
//invoke default serialization method
}
在反序列化時(shí),如果類的定義發(fā)生了變化,版本號(hào)就會(huì)變化,與流中的版本號(hào)就會(huì)不匹配,反序列化就會(huì)拋出異常,類型為java.io.InvalidClassException。通常情況下,我們希望自定義這個(gè)版本號(hào),而非讓Java自動(dòng)生成,一方面是為了更好的控制,另一方面是為了性能,因?yàn)镴ava自動(dòng)生成的性能比較低。
四、Parcelable接口(Android專用)
實(shí)現(xiàn)Parcelable步驟
1、implements Parcelable
2、重寫writeToParcel方法,將你的對(duì)象序列化為一個(gè)Parcel對(duì)象,即:將類的數(shù)據(jù)寫入外部提供的Parcel中,打包需要傳遞的數(shù)據(jù)到Parcel容器保存,以便從 Parcel容器獲取數(shù)據(jù)
3、重寫describeContents方法,內(nèi)容接口描述,默認(rèn)返回0就可以
4、實(shí)例化靜態(tài)內(nèi)部對(duì)象CREATOR實(shí)現(xiàn)接口Parcelable.Creator
public static final Parcelable.Creator CREATOR
注:其中public static final一個(gè)都不能少,內(nèi)部對(duì)象CREATOR的名稱也不能改變,必須全部大寫。需重寫本接口中的兩個(gè)方法:createFromParcel(Parcel in) 實(shí)現(xiàn)從Parcel容器中讀取傳遞數(shù)據(jù)值,封裝成Parcelable對(duì)象返回邏輯層,newArray(int size) 創(chuàng)建一個(gè)類型為T,長(zhǎng)度為size的數(shù)組,僅一句話即可(return new T[size]),供外部類反序列化本類數(shù)組使用。
簡(jiǎn)而言之:通過(guò)writeToParcel將你的對(duì)象映射成Parcel對(duì)象,再通過(guò)createFromParcel將Parcel對(duì)象映射成你的對(duì)象。也可以將Parcel看成是一個(gè)流,通過(guò)writeToParcel把對(duì)象寫到流里面,在通過(guò)createFromParcel從流里讀取對(duì)象,只不過(guò)這個(gè)過(guò)程需要你來(lái)實(shí)現(xiàn),因此寫的順序和讀的順序必須一致。
示例代碼:
import android.os.Parcel;
import android.os.Parcelable;
/**
* @author mumuxi
* @version 2022/4/29
*/
public class TestParcelable implements Parcelable {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.age);
dest.writeString(this.name);
}
public void readFromParcel(Parcel source) {
this.age = source.readInt();
this.name = source.readString();
}
public TestParcelable() {
}
protected TestParcelable(Parcel in) {
this.age = in.readInt();
this.name = in.readString();
}
public static final Parcelable.Creator<TestParcelable> CREATOR =
new Parcelable.Creator<TestParcelable>() {
@Override
public TestParcelable createFromParcel(Parcel source) {
return new TestParcelable(source);
}
@Override
public TestParcelable[] newArray(int size) {
return new TestParcelable[size];
}
};
}
補(bǔ)充:可以通過(guò)Android Studio插件一鍵生成Parcelable代碼(Android Parcelable code generator插件)。寫完實(shí)體類后在Generate界面(Alt + Insert鍵)選擇Parcelable一鍵生成。
五、Parcelable和Serializable的區(qū)別
1、作用
Serializable的作用是為了保存對(duì)象的屬性到本地文件、數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)流、RMI(Remote Method Invocation)以方便數(shù)據(jù)傳輸,當(dāng)然這種傳輸可以是程序內(nèi)的也可以是兩個(gè)程序間的。而Android的Parcelable的設(shè)計(jì)初衷是因?yàn)镾erializable效率過(guò)慢,為了在程序內(nèi)不同組件間以及不同Android程序間高效的傳輸數(shù)據(jù)而設(shè)計(jì),這些數(shù)據(jù)僅在內(nèi)存中存在,Parcelable是通過(guò)IBinder通信的消息的載體。
2、效率及選擇
Serializable是Java中的序列化接口,其使用起來(lái)簡(jiǎn)單但開(kāi)銷較大(因?yàn)镾erializable在序列化過(guò)程中使 用了反射機(jī)制,故而會(huì)產(chǎn)生大量的臨時(shí)變量,從而導(dǎo)致頻繁的GC),并且在讀寫數(shù)據(jù)過(guò)程中,它是通 過(guò)IO流的形式將數(shù)據(jù)寫入到硬盤或者傳輸?shù)骄W(wǎng)絡(luò)上。Parcelable的性能比Serializable好,在內(nèi)存開(kāi)銷方面較小,所以在內(nèi)存間數(shù)據(jù)傳輸時(shí)推薦使用Parcelable,如activity間傳輸數(shù)據(jù)。而Serializable可將數(shù)據(jù)持久化方便保存,Parcelable不能很好的保證數(shù)據(jù)的持續(xù)性,使用相對(duì)復(fù)雜,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時(shí)選擇Serializable。