為什么使用Serializable(序列化)
經(jīng)??吹接行╊愓{(diào)用了Serializable接口,而有些類又沒(méi)有調(diào)用Serializable接口。那么什么情況下要調(diào)用Serializable接口。
首先來(lái)了解一下Serializable。(類通過(guò)實(shí)現(xiàn) java.io.Serializable 接口以啟用其序列化功能。未實(shí)現(xiàn)此接口的類將無(wú)法使其任何狀態(tài)序列化或反序列化。序列化接口Serializable沒(méi)有方法或字段,僅用于標(biāo)識(shí)可序列化的語(yǔ)義)
實(shí)現(xiàn)了Serializable接口的對(duì)象,可將它們轉(zhuǎn)換成一系列字節(jié),并可在以后完全恢復(fù)回原來(lái)的樣子。這一過(guò)程亦可通過(guò)網(wǎng)絡(luò)進(jìn)行。這意味著序列化機(jī)制能自動(dòng)補(bǔ)償操作系統(tǒng)間的差異。換句話說(shuō),可以先在Windows機(jī)器上創(chuàng)建一個(gè)對(duì)象,對(duì)其序列化,然后通過(guò)網(wǎng)絡(luò)發(fā)給一臺(tái)Unix機(jī)器,然后在那里準(zhǔn)確無(wú)誤地重新“裝配”。不必關(guān)心數(shù)據(jù)在不同機(jī)器上如何表示,也不必關(guān)心字節(jié)的順序或者其他任何細(xì)節(jié)。
serialization主要用來(lái)支持2種主要的特性:
1、RMI(Remote method invocation)。
RMI允許象在本機(jī)上一樣操作遠(yuǎn)程機(jī)器上的對(duì)象。當(dāng)發(fā)送消息給遠(yuǎn)程對(duì)象和調(diào)用遠(yuǎn)程方法時(shí),就需要用到serializaiton機(jī)制來(lái)發(fā)送參數(shù)和接收返回值。
2、保存信息。
在某個(gè)時(shí)候把狀態(tài)信息保存起來(lái),以便以后某個(gè)時(shí)候能恢復(fù)這些狀態(tài)信息。
Hibernaet和EJB中的實(shí)體Bean就用到了上面兩個(gè)特性。
另外:保存的時(shí)候不僅能保存對(duì)象的副本,而且還會(huì)把對(duì)象里面所引用的對(duì)象也保存起來(lái),以此類推。就像在編譯某個(gè)類一樣,會(huì)涉及到所用到的所有類。但是所引用的對(duì)象也必須是可序列化的,不然會(huì)拋NotSerializableException異常。
下面來(lái)寫個(gè)例子:(A和B類都是可序列化的,WriteObj:將A序列化,ReadObj:將A反序列化)
class A:
- package woxingwosu;
- import java.io.Serializable;
- public class A implements Serializable{
- private String name="my name is a";
- private B b=null;
- A(){
- b=new B();
- }
- public B getB() {
- return b;
- }
- public void setB(B b) {
- this.b = b;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String show(){
- return "a.toString <a.name=\""+this.name+"\" a.b.name=\""+this.b.getName()+"\">"
- +"\na="+this.toString()+" b="+this.b.toString();
- }
- }
class B:
- package woxingwosu;
- import java.io.Serializable;
- public class B implements Serializable{
- private String name="my name is B";
- B(){}
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
class WriteSeri:
- package woxingwosu;
- import java.io.FileOutputStream;
- import java.io.ObjectOutputStream;
- /**
- * 寫Object(系列化)
- * @author 我行我素
- */
- public class WriteSeri {
- public static void main(String[] args) {
- ObjectOutputStream outObj=null;
- try{
- FileOutputStream outStr=new FileOutputStream("obj.txt");
- outObj=new ObjectOutputStream(outStr);
- A a=new A();
- outObj.writeObject(a);
- System.out.println("write obj :"+a.show());
- outObj.flush();
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- try{
- if(outObj!=null)
- outObj.close();
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }}
class ReadSeri:
- package woxingwosu;
- import java.io.FileInputStream;
- import java.io.ObjectInputStream;
- /**
- * 讀Object(反系列化)
- * @author 我行我素
- */
- public class ReadSeri {
- public static void main(String[] args) {
- ObjectInputStream inObj=null;
- try{
- FileInputStream inStr=new FileInputStream("obj.txt");
- inObj=new ObjectInputStream(inStr);
- A a=(A)inObj.readObject();
- System.out.println("read Object :"+a.show());
- }catch(Exception e){
- e.printStackTrace();
- }finally{
- try{
- if(inObj!=null)
- inObj.close();
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
- }
首先,我們運(yùn)行WriteObj,實(shí)現(xiàn)序列化,得到輸出結(jié)果
- write obj :a.toString <a.name="my name is a" a.b.name="my name is B">
- a=woxingwosu.A@a90653 b=woxingwosu.B@de6ced
然后我們?cè)龠\(yùn)行ReadObj,實(shí)現(xiàn)反序列化,得到輸出結(jié)果
- read Object :a.toString <a.namea.name="my name is a" a.b.name="my name is B">
- a=woxingwosu.A@a90653 b=woxingwosu.B@de6ced
遺漏了一個(gè)問(wèn)題,就是標(biāo)識(shí)版本的serialVersionUID。
官方文檔:如果可序列化類未顯式聲明 serialVersionUID,則序列化運(yùn)行時(shí)將基于該類的各個(gè)方面計(jì)算該類的默認(rèn) serialVersionUID 值,如“Java(TM) 對(duì)象序列化規(guī)范”中所述。不過(guò),強(qiáng)烈建議 所有可序列化類都顯式聲明 serialVersionUID 值,原因計(jì)算默認(rèn)的 serialVersionUID 對(duì)類的詳細(xì)信息具有較高的敏感性,根據(jù)編譯器實(shí)現(xiàn)的不同可能千差萬(wàn)別,這樣在反序列化過(guò)程中可能會(huì)導(dǎo)致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實(shí)現(xiàn)的一致性,序列化類必須聲明一個(gè)明確的 serialVersionUID 值。還強(qiáng)烈建議使用 private 修改器顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應(yīng)用于立即聲明類 -- serialVersionUID 字段作為繼承成員沒(méi)有用處。
剛才寫的例子中就沒(méi)有用到serialVersionUID,這時(shí)JVM會(huì)根據(jù)類名、接口名、成員方法及屬性等來(lái)生成一個(gè)64位的哈希字段作為serialVersionUID。但是如果序列化和反序列化的JVM版本不一樣的話,還是顯示寫上serialVersionUID安全。
以上是個(gè)人看法,如有錯(cuò)誤之處,還望指出。
原文地址:http://www.blogjava.net/woxingwosu/archive/2007/07/12/129511.html
【編輯推薦】