- 在android中為什么要序列化传轰?
答:在android中傳遞對象的時(shí)候谷婆,如果直接傳遞的話辽聊,接受方無法識(shí)別跟匆,因此需要序列化通砍,將對象轉(zhuǎn)換成流的形式傳遞。在另一方利用反序列化解析出來迹冤。(自己理解的泡徙,如有錯(cuò)誤請高手指出)
- 什么情況下需要序列化膜蠢?
a)當(dāng)你想把內(nèi)存中的對象寫入到硬盤的時(shí)候;
b)當(dāng)你想用套接字在網(wǎng)絡(luò)上傳送對象的時(shí)候礁竞;
c)當(dāng)你想通過RMI傳輸對象的時(shí)候杉辙;
再稍微解釋一下:
a)比如說你的內(nèi)存不夠用了,那計(jì)算機(jī)就要將內(nèi)存里面的一部分對象暫時(shí)的保存到硬盤中枫绅,等到要用的時(shí)候再讀入到內(nèi)存中硼端,硬盤的那部分存儲(chǔ)空間就是所謂的虛擬內(nèi)存。在比如過你要將某個(gè)特定的對象保存到文件中珍昨,我隔幾天在把它拿出來用,那么這時(shí)候就要實(shí)現(xiàn)Serializable接口兔毙;
b)在進(jìn)行java的Socket編程的時(shí)候兄春,你有時(shí)候可能要傳輸某一類的對象,那么也就要實(shí)現(xiàn)Serializable接口哑姚;最常見的你傳輸一個(gè)字符串,它是JDK里面的類叙量,也實(shí)現(xiàn)了Serializable接口绞佩,所以可以在網(wǎng)絡(luò)上傳輸。
c)如果要通過遠(yuǎn)程的方法調(diào)用(RMI)去調(diào)用一個(gè)遠(yuǎn)程對象的方法析既,如在計(jì)算機(jī)A中調(diào)用另一臺(tái)計(jì)算機(jī)B的對象的方法谆奥,那么你需要通過JNDI服務(wù)獲取計(jì)算機(jī)B目標(biāo)對象的引用酸些,將對象從B傳送到A檐蚜,就需要實(shí)現(xiàn)序列化接口。
在NewsListActivity.java中
<pre style="box-sizing: border-box; outline: 0px; margin: 0px 0px 24px; padding: 8px; position: relative; font-family: Consolas, Inconsolata, Courier, monospace; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">
Intent intent = new Intent(NewsListActivity.this, NewsInfoActivity.class);
intent.putExtra("ItemState", item);
NewsListActivity.this.startActivity(intent);
</pre>
在NewsInfoActivity.java中
<pre style="box-sizing: border-box; outline: 0px; margin: 0px 0px 24px; padding: 8px; position: relative; font-family: Consolas, Inconsolata, Courier, monospace; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">
if(intent.getExtras() != null){
item = (RSSItem)intent.getExtras().getSerializable("ItemState");
}
</pre>
這里要求RSSItem類必須能序列化,即實(shí)現(xiàn)Serializable接口
我的RSSItem繼承RSSObject類咳短,而RSSObject類沒有實(shí)現(xiàn)Serializable接口
<pre style="box-sizing: border-box; outline: 0px; margin: 0px 0px 24px; padding: 8px; position: relative; font-family: Consolas, Inconsolata, Courier, monospace; white-space: pre-wrap; word-wrap: break-word; overflow-x: auto; font-size: 14px; line-height: 22px; color: rgb(0, 0, 0); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(255, 255, 255); text-decoration-style: initial; text-decoration-color: initial;">
public class RSSItem extends RSSObject implements Serializable
</pre>
現(xiàn)在問題出現(xiàn)了咙好,沒有報(bào)編譯錯(cuò)誤,也沒出現(xiàn)運(yùn)行時(shí)錯(cuò)誤嘹悼,但就是NewsInfoActivity拿到的item的很多屬性為null
解決:父類RSSObject必須實(shí)現(xiàn)Serializable接口层宫,就沒問題了。
http://www.cjsdn.net/post/view?bid=2&id=192312&sty=1&tpg=5&age=0
一限匣、序列化簡介
在這里我先簡要介紹一下和序列化相關(guān)的一些概念膛腐,如果你覺得自己對序列化已經(jīng)比較熟悉,那么跳過這一節(jié)哲身,直接看下一節(jié)內(nèi)容。
序列化是Java中的一個(gè)非常重要的特性怔揩,通過序列化機(jī)制粱腻,我們可以將Java的對象變成流宠进,或者存儲(chǔ)在硬盤中,或者通過網(wǎng)絡(luò)傳輸給網(wǎng)絡(luò)的其他用戶材蹬。而序列化在RMI,EJB中都有應(yīng)用昆庇,可以說是構(gòu)成J2EE的一個(gè)重要的技術(shù)闸溃。
- 1)Serializable接口
如果想讓一個(gè)類可被序列化,那么這個(gè)類必須實(shí)現(xiàn)Serializable接口表蝙,這個(gè)接口本身沒有任何方法和屬性乓旗,它的作用只是為了標(biāo)示一個(gè)類可以被序列化,這一個(gè)特性好像在Java里用的比較多欲诺,比如克隆也是采用了相同的機(jī)制扰法。
因此,如果我們想創(chuàng)建一個(gè)可以被序列化的類塞颁,我們可以像下面的代碼一樣做祠锣。
import java.io.Serializable;
public class SerialClass implements Serializable
{
private static final long serialVersionUID = -190734710746841476L;
private String c;
public int a;
public String b;
public int getA(){return a;}
public String getB(){return b;}
public void setA(int a){this.a = a;}
public void setB(String b){this.b = b;}
public String getC(){return c; }
public void setC(String c){this.c = c; }
從上面的例子我們可以看到,除了需要實(shí)現(xiàn)Serialzable接口外蓬推,一個(gè)可序列化的類和一個(gè)普通類沒有什么大的區(qū)別澡腾。不過我們還是有一些特別的東西需要注意的。
- 2)屬性 serialVersionUID
這個(gè)屬性是一個(gè)私有的靜態(tài)final屬性毅糟,一般剛接觸序列化的人會(huì)忽略這個(gè)屬性澜公,因?yàn)檫@個(gè)屬性不是必須的。這個(gè)屬性主要是為了保證一個(gè)可序列 化類在不同的 Java編譯器中迹辐,能保證相同的版本號糊渊,這樣保證當(dāng)這個(gè)類在不同的JVM中進(jìn)行序列化與反序列化時(shí)不會(huì)出現(xiàn)InvalidClassException異 常渺绒。
- 3)屬性與序列化
那么我們再考慮另一個(gè)問題菱鸥,是不是一個(gè)類實(shí)現(xiàn)了Serializable之后就可以看成是可序列化的類呢?答案是不行殷绍,實(shí)際上一個(gè)類是否可序 列化還需要看這個(gè)類的屬性鹊漠,在Java規(guī)范中主到,一個(gè)類是否能序列化登钥,不但取決于這個(gè)類是否實(shí)現(xiàn)Serializable接口娶靡,還取決于這個(gè)類中的屬性是否 可序列化。在上面的例子里塔鳍,一共有三個(gè)屬性轮纫,兩個(gè)是String對象,一個(gè)是int類型的屬性蜡感,實(shí)際上郑兴,String類是可序列化的(繼承了 Serializable 接口且它的屬性都是可序列化的),而像int這樣的基本數(shù)據(jù)類型在Java中被看成是可序列化的叽粹,因此我們可以確認(rèn)上面的這個(gè)類是一個(gè)可序列化的類却舀。那么 現(xiàn)在我們已經(jīng)創(chuàng)建了一個(gè)可序列化類,接下去的問題是怎么使用這個(gè)類辆脸,對它進(jìn)行序列化與反序列化操作螃诅?
- 4)ObjectOutputStream 和 ObjectInputStream類
Jdk提供了兩個(gè)IO流類進(jìn)行序列化和反序列化操作,其中ObjectOutputStream中的writeObject方法對一個(gè)對象進(jìn) 行序列化工作倘是;而ObjectInputStrem中的readObject方法則負(fù)責(zé)發(fā)序列化的操作袭艺。下面的例子完整的顯示了一個(gè)類的序列化和反序列化 操作。
package serialtest;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class TestClass {
public static void main(String[] args) throws Exception {
SerialClass s = new SerialClass();
s.setA(10);
s.setB("hello");
s.setC("world");
//創(chuàng)建ObjectOutputStream對象瘤睹,準(zhǔn)備序列化對象s
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("abc"));
//調(diào)用writeObject將對象進(jìn)行序列化默蚌,存儲(chǔ)在文件abc中苇羡。
oos.writeObject;
oos.flush();
oos.close();
//創(chuàng)建ObjectInputStream對象鼻弧,準(zhǔn)備從文件abc中反序列化SerialClass對象
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("abc"));
//調(diào)用readObject將存儲(chǔ)在文件abc中的字節(jié)流反序列化為SerialClass對象攘轩。
s = (SerialClass) ois.readObject();
System.out.println(s.getA());
System.out.println(s.getB());
System.out.println(s.getC());
}
}
執(zhí)行程序码俩,最后的結(jié)果應(yīng)該是:
10
Hello
World
好了,序列化的基礎(chǔ)知識(shí)就講到這里笨篷。接下來我們看看繼承對于序列化的影響瓣履。
二、繼承對序列化的影響
我們現(xiàn)在把上面的例子做點(diǎn)變換冕臭,變成如下的形式燕锥。
- 1)首先創(chuàng)建一個(gè)Interface。
package serialtest;
public interface SuperInterface
{
public int getA();
public String getB();
public void setA(int a);
public void setB(String b);
}
- 2)創(chuàng)建一個(gè)抽象類實(shí)現(xiàn)SuperInterface接口托慨,注意暇榴,這個(gè)類沒有實(shí)現(xiàn)Serializable接口。
package serialtest;
public abstract class AbsSuperClass implements SuperInterface
{
public int a;
public String b;
public int getA() {return a;}
public String getB(){return b; }
public void setA(int a) {this.a = a;}
public void setB(String b) {this.b = b;}
}
- 3)SerialClass類繼承了AbSuperClass窟感,同時(shí)它也實(shí)現(xiàn)了Serializable接口歉井。
package serialtest;
import java.io.Serializable;
public class SerialClass extends AbsSuperClass implements Serializable
{
private static final long serialVersionUID = -190734710746841476L;
private String c;
public String getC(){return c; }
public void setC(String c) {this.c = c;}
}
這時(shí)候我們在運(yùn)行Test哩至,將會(huì)發(fā)現(xiàn)結(jié)果和第一節(jié)的例子不一樣了蜜自,這時(shí)候結(jié)果將變成:
0
null
world
而導(dǎo)致這個(gè)結(jié)果的原因就在AbSuperClass上,因?yàn)锳bSuperClass沒有實(shí)現(xiàn)Serializable接口箭阶,所以它不可被序 列化;但 SerialClass由于實(shí)現(xiàn)了Serializable接口而成為一個(gè)可序列化的類嘹叫,這時(shí)候诈乒,屬性c因?yàn)槭荢erialClass的類所以c的值在序 列化時(shí)將被保存,而屬性a和b是從AbSuperClass中繼承而來喂饥,他們因?yàn)锳bSuperClass的緣故而導(dǎo)致了它們的值不能在序列化是被保存肠鲫。
關(guān)于這一點(diǎn),我在Java文檔中也得到了證實(shí)集侯,在Java doc中明確指出帜消,如果一個(gè)類繼承了一個(gè)非Serializable類時(shí),如果想在序列化中保存父類的屬性泡挺,則需要實(shí)現(xiàn)額外的代碼顯式地存儲(chǔ)父類的屬性值。
最后再回到本文的初衷娄猫,誠然,這篇文章是以對象的序列化為主的媳溺,但最后我又要回到一個(gè)比較古老的話題上,那就是在軟件開發(fā)的時(shí)候扯躺,繼承到底該用到什么一種程度蝎困?畢竟,濫用繼承對整個(gè)系統(tǒng)的影響將是深遠(yuǎn)的澎埠。
Java中serialVersionUID的解釋serialVersionUID作用: 序列化時(shí)為了保持版本的兼容性始藕,即在版本升級時(shí)反序列化仍保持對象的唯一性氮趋。
Java的序列化機(jī)制是通過在運(yùn)行時(shí)判斷類的serialVersionUID來驗(yàn)證版本一致性的凭峡。在進(jìn)行反序列化時(shí)决记,JVM會(huì)把傳來的字節(jié)流中的serialVersionUID與本地相應(yīng)實(shí)體(類)的serialVersionUID進(jìn)行比較,如果相同就認(rèn)為是一致的索昂,可以進(jìn)行反序列化扩借,否則就會(huì)出現(xiàn)序列化版本不一致的異常。
有兩種生成方式:
一個(gè)是默認(rèn)的1L康谆,比如:private static final long serialVersionUID = 1L;
一個(gè)是根據(jù)類名嫉到、接口名、成員方法及屬性等來生成一個(gè)64位的哈希字段孽锥,比如: private static final long serialVersionUID = xxxxL;
當(dāng)你一個(gè)類實(shí)現(xiàn)了Serializable接口细层,如果沒有定義serialVersionUID,Eclipse會(huì)提供這個(gè) 提示功能告訴你去定義 盛撑。在Eclipse中點(diǎn)擊類中warning的圖標(biāo)一下捧搞,Eclipse就會(huì) 自動(dòng)給定兩種生成的方式。如果不想定義它陌僵,在Eclipse的設(shè)置中也 可以把它關(guān)掉的创坞,設(shè)置如下: Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==> Potential programming problems 將Serializable class without serialVersionUID的warning改成ignore即可受葛。
如果你沒有考慮到兼容性問題時(shí)偎谁,就把它關(guān)掉巡雨,不過有這個(gè)功能是好的席函,只要任何類別實(shí)現(xiàn)了Serializable這個(gè)接口的話,如果沒有加入serialVersionUID正蛙,Eclipse都會(huì)給你warning提示营曼,這個(gè)serialVersionUID為了讓該類別Serializable向后兼容。如果你的類Serialized存到硬盤上面后锻全,可是后來你卻更改了類別的field(增加或減少或改名)录煤,當(dāng)你Deserialize時(shí),就會(huì)出現(xiàn)Exception的部翘,這樣就會(huì)造成不兼容性的問題响委。但當(dāng)serialVersionUID相同時(shí),它就會(huì)將不一樣的field以type的預(yù)設(shè)值Deserialize赘风,可避開不兼容性問題。