一、序列化
在java中,序列化是指將對(duì)象轉(zhuǎn)化為字節(jié)序列的過(guò)程。而反序列化則是將字節(jié)序列轉(zhuǎn)為對(duì)象。換句話說(shuō),序列化就是將對(duì)象進(jìn)行流化。序列化的作用:
- 永久性保存對(duì)象,保存對(duì)象的字節(jié)序列到本地文件中;
- 通過(guò)序列化對(duì)象在網(wǎng)絡(luò)中傳遞對(duì)象;
- 通過(guò)序列化在進(jìn)程間傳遞對(duì)象。
- 通過(guò)序列化在intent中傳遞自定義對(duì)象。
二、如何使用Serializable接口
Serializable是一個(gè)空接口,使用起來(lái)相當(dāng)簡(jiǎn)單,只要在所需序列化的類(lèi)實(shí)現(xiàn)這個(gè)接口就可以了,系統(tǒng)會(huì)幫你實(shí)現(xiàn)對(duì)象的序列化。
我們舉個(gè)簡(jiǎn)單的例子
public class Person implements Serializable{
public static final long serialVersionUID = 1L;
public String name;
public int age;
public String sex;
....
}
通過(guò)IO流可輕松進(jìn)行序列化和反序列化。
//序列化過(guò)程
Person person = new Person("Tim",25,"male");
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("Tim.txt"));
output.writeObject(person);
output.close();
//反序列化過(guò)程
ObjectInputStream input = new ObjectInputStream(new FileInputStream("Tim.txt"));
Person person = (Person)input.readObject();
input.close();
如果有多個(gè)對(duì)象寫(xiě)入同一個(gè)文件,為了正確讀取數(shù)據(jù),完成反序列化,必須保證向?qū)ο筝敵隽鲗?xiě)對(duì)象的順序與從對(duì)象輸入流中讀對(duì)象的順序一致。
值得注意的是,雖然通過(guò)反序列化恢復(fù)得到了對(duì)象,但是和序列化前的對(duì)象并非同一對(duì)象。
有細(xì)心的同學(xué)發(fā)現(xiàn)了我們?cè)陬?lèi)的聲明中加上了一行
private static final long serialVersionUID = 1L;
這句話有什么用呢?其實(shí)這句話并非必須的,沒(méi)有這句話也可以序列化。只是這句話會(huì)對(duì)反序列造成一定的影響。
序列化的時(shí)候系統(tǒng)會(huì)將serialVersonUID一同寫(xiě)入序列化的文件,當(dāng)反序列的時(shí)候系統(tǒng)會(huì)檢測(cè)文件中的serialVersonUID,只有和當(dāng)前類(lèi)的serialVersonUID值一致時(shí),反序列化才能成功,否則程序會(huì)crash.
serialVersionUID的賦值有兩種方式,一種是默認(rèn)值,比如上面的1L;
另一種是根據(jù)當(dāng)前類(lèi)的結(jié)構(gòu)生成hash值。無(wú)論是哪種方式都可以,只要我們顯示定義UID,就可以反序列化成功。
如果沒(méi)有顯示定義UID,系統(tǒng)自動(dòng)產(chǎn)生一個(gè)hash值,當(dāng)序列化后,如果類(lèi)的結(jié)構(gòu)發(fā)生變化,比如增加了某個(gè)成員變量,那么UID的值也會(huì)改變,這時(shí)當(dāng)前類(lèi)的UID和序列化文件中的UID就不一致了,程序就會(huì)報(bào)錯(cuò)。所以建議手動(dòng)給UID賦值。
但有一種特例,即使前后UID一致,反序列化也會(huì)失敗,即類(lèi)發(fā)生了特殊的改變,比如修改類(lèi)名,改變成員變量的類(lèi)型。
有時(shí)候我們并不想讓所以成員變量進(jìn)行序列化,這時(shí)我們只要在變量聲明前加上transient關(guān)鍵字,就可將其排除在外.
private transient String sex;
還有一點(diǎn)要注意,靜態(tài)成員不屬于對(duì)象,所以不參與序列化。
二、Parcelable接口
android為我們提供了新的序列化方式,即parcelable接口。parcelable使用起來(lái)比較麻煩,但是相對(duì)于serializable效率比較高。為什么這樣說(shuō)呢?因?yàn)閟erializable使用了大量的IO流操作。parcelable是android上的序列化方式,比較適合用在android開(kāi)發(fā)上。
接下來(lái)我們看看如何使用Parcelable接口
public class Person implements Parcelable{
private static final long serialVersionUID = 1L;
public String name;
public int age;
public String sex;
public Person(String name,int age,String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public int describeContents(){
return 0 ;
}
public void writeToParcel(Parcel out,int flags){
out.writeString(name);
out.writeInt(age);
out.writeString(sex);
}
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>(){
public Person createFromParcel(Parcel in){
return new Person(in);
}
public Person[] newArray(int size){
return new Person[size];
}
};
private Person(parcel in){
name = in.readString();
age = in.readInt();
sex = in.readString();
}
}
這是典型的Parcelable序列化的寫(xiě)法,從上面代碼看出,writeToParcel方法實(shí)現(xiàn)了序列化,Parcel保存了序列化的數(shù)據(jù)。
反序列化由CREATOR完成,read方法讀取parcel內(nèi)的數(shù)據(jù),完成反序列化。
describeContents方法用于描述當(dāng)前對(duì)象內(nèi)容,如果含有文件描述符,返回1,大部分情況下都返回0;
writeToParcel的flags參數(shù)取值為0或1;1表示不能立刻釋放資源,大部分情況下取0;
補(bǔ)充:Serializable和Parcelable不僅可以使用在進(jìn)程間通信,還可以用于Intent的數(shù)據(jù)傳輸。
方法很簡(jiǎn)單,
intent.putExtra(key,object);
object傳入一個(gè)序列化對(duì)象。獲取數(shù)據(jù)時(shí),如果是Serializable對(duì)象,使用
getIntent().getSerializableExtra(key);
如果是Parcelable對(duì)象,使用
getIntent().getParcelableExtra(key).
好了,我所知道的Serializable和Parcelable知識(shí)基本就講完了。這是我第一次在簡(jiǎn)書(shū)上寫(xiě)文章,如果有什么寫(xiě)得不好的地方,歡迎指正。