Object serialization的定義:
Object serialization 允許你將實現了Serializable接口的對象轉換爲字節序列,這些字節序列可以被完全存儲以備以後重新生成原來的對象。
serialization不但可以在本機做,而且可以經由網絡操作(就是貓小說的RMI)。這個好處是很大的----因爲它自動屏蔽了操作系統的差異,字節順序(用Unix下的c開發過網絡編程的人應該知道這個概念,我就容易在這上面犯錯)等。比如,在Window平臺生成一個對象並序列化之,然後通過網絡傳到一臺Unix機器上,然後可以在這臺Unix機器上正確地重構這個對象。
Object serialization主要用來支持2種主要的特性:
1。Java的RMI(remote method invocation).RMI允許象在本機上一樣操作遠程機器上的對象。當發送消息給遠程對象時,就需要用到serializaiton機制來發送參數和接收返回直。
2。Java的JavaBeans. Bean的狀態信息通常是在設計時配置的。Bean的狀態信息必須被存起來,以便當程序運行時能恢復這些狀態信息。這也需要serializaiton機制。
二。sakulagi和rollingpig說的持久化我也說一下。
我覺得你們說的應該是英文裏的persistence.但是Java語言裏現在只支持lightweight persistence,就是輕量級持久化,這是通過serialization機制來實現的。
persistence是指一個對象的生命週期不由程序是否執行來決定,即使是在程序終止時這個對象也存在。它把一個serializable的對象寫到磁盤(本機或其他機器上的非RAM存儲器),並在程序重新調用時再讀取對象到通常的RAM存儲器。
爲什麼說Java的serialization機制實現的是lightweight persistence?因爲你必須顯式的序列化和反序列化程序裏的對象;而不是直接由一個關鍵詞來定義一個對象是序列化的然後由系統做相應的處理。 如果以後的Java版本出現一個新的關鍵字來實現這種機制,比如就是persistence,如果我用
persistence (String s="chinaunix")
然後系統自動做貓小程序裏的那些處理,那麼Java就實現了persistence.
簡單來說,Java的序列化機制是通過在運行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體(類)的serialVersionUID進行比較,如果相同就認爲是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常。(InvalidCastException)
serialVersionUID有兩種顯示的生成方式: 一個是默認的1L,比如:private static final long serialVersionUID = 1L; 一個是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段,比如: private static final long serialVersionUID = xxxxL; 當你一個類實現了Serializable接口,如果沒有顯示的定義serialVersionUID,Eclipse會提供這個 提示功能告訴你去定義 。在Eclipse中點擊類中warning的圖標一下,Eclipse就會 自動給定兩種生成的方式。如果不想定義它,在Eclipse的設置中也 可以把它關掉的,設置如下: Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==> Potential programming problems 將Serializable class without serialVersionUID的warning改成ignore即可。
當實現java.io.Serializable接口的實體(類)沒有顯式地定義一個名爲serialVersionUID,類型爲long的變量時,Java序列化機制會根據編譯的class(它通過類名,方法名等諸多因素經過計算而得,理論上是一一映射的關係,也就是唯一的)自動生成一個serialVersionUID作序列化版本比較用,這種情況下,如果class文件(類名,方法明等)沒有發生變化(增加空格,換行,增加註釋,等等),就算再編譯多次,serialVersionUID也不會變化的.
如果我們不希望通過編譯來強制劃分軟件版本,即實現序列化接口的實體能夠兼容先前版本,未作更改的類,就需要顯式地定義一個名爲serialVersionUID,類型爲long的變量,不修改這個變量值的序列化實體都可以相互進行串行化和反串行化。
如果你沒有考慮到兼容性問題時,就把它關掉,不過有這個功能是好的,只要任何類別實現了Serializable這個接口的話,如果沒有加入 serialVersionUID,Eclipse都會給你warning提示,這個serialVersionUID爲了讓該類別 Serializable向後兼容。
問題一:假設有A端和B端,如果2處的serialVersionUID不一致,會產生什麼錯誤呢?
問題二:假設2處serialVersionUID一致,如果A端增加一個字段,B端不變,會是什麼情況呢?
問題三:假設2處serialVersionUID一致,如果B段增加一個字段,A端不變,會是什麼情況呢?
問題四:假設2處serialVersionUID一致,如果A端減少一個字段,B端不變,會是什麼情況呢?
問題五:假設2處serialVersionUID一致,如果B端減少一個字段,A端不變,會是什麼情況呢?
例子如下:寫2個類,類名相同,字段相同,方法相同.放在不同的包裏,來模仿A端和B端.
實體類:在本例中,在測試類SerialTest執行前代表A端,然後,在測試類DeserialTest執行前代表B端.
package com.test; import java.io.Serializable;
public class Serial implements Serializable{ /** * */ private static final long serialVersionUID = 6977402643848374753L; int id; String name; public Serial(int id, String name) { this.id = id; this.name = name; } public String toString() { return "DATA: " + id + " " +name; } }
測試類,代表A端的序列化
package com.test.serializable;
import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream;
public class SerialTest {
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Serial serial1 = new Serial(1,"song"); System.out.println("Object Serial"+serial1); try { FileOutputStream fos = new FileOutputStream("serialTest.txt"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(serial1); oos.flush(); oos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
測試類,代表B端的反序列化
package com.test.serializable;
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream;
public class DeserialTest {
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Serial serial2 ; try { FileInputStream fis = new FileInputStream("serialTest.txt"); ObjectInputStream ois = new ObjectInputStream(fis); serial2 = (Serial)ois.readObject(); ois.close(); System.out.println("Object Deserial"+serial2); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
}
問題一:假設有A端和B端,如果2處的serialVersionUID不一致,會產生什麼錯誤呢?
答案如下:
1)先執行測試類SerialTest,然後修改serialVersion值(或註釋掉serialVersion並編譯),再執行測試類DeserialTest,報錯:
java.io.InvalidClassException: com.test.serializable.Serial; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 11
2)A端和B端都沒顯示的寫serialVersionUID,實體類沒有改動(如果class文件(類名,方法明等)沒有發生變化(增加空格,換行,增加註釋,等等),).序列化,反序列化正常.
問題二:假設2處serialVersionUID一致,如果A端增加一個字段,B端不變,會是什麼情況呢?
答案二: 序列化,反序列化正常,A端增加的字段丟失(被B端忽略).
問題五:假設2處serialVersionUID一致,如果B端減少一個字段,A端不變,會是什麼情況呢?
答案:與問題二類似,序列化,反序列化正常,B端字段少於A端,A端多的字段值丟失(被B端忽略).
問題三:假設2處serialVersionUID一致,如果B段增加一個字段,A端不變,會是什麼情況呢?
問題四:假設2處serialVersionUID一致,如果A端減少一個字段,B端不變,會是什麼情況呢?(與問題三類似,四答案:序列化,反序列化正常,B端字段多餘A端,B端多出的字段被賦予對應類型的默認值)
答案三: 序列化,反序列化正常,B端新增加的int字段被賦予了默認值0.
例子如下:
3)先執行SerialTest,然後在實體類增加一個字段age,再執行測試類DeserialTest.
package com.test.serializable;
import java.io.Serializable;
public class Serial implements Serializable {
/** * */ private static final long serialVersionUID = -2337937881709830076L;
/** * */ //private static final long serialVersionUID = 1L; int id;
String name; public Serial(int id, String name) { this.id = id; this.name = name; } public String toString() { return "DATA: " + id + " " +name; } public int age ;//在B端增加一個新字段 }
相應的修改測試類DeserialTest,打印出age的值.
package com.test.serializable;
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.ObjectInputStream;
public class DeserialTest {
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub Serial serial2 ; try { FileInputStream fis = new FileInputStream("serialTest.txt"); ObjectInputStream ois = new ObjectInputStream(fis); serial2 = (Serial)ois.readObject(); ois.close(); System.out.println("Object Deserial"+serial2+" age="+serial2.age); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
}
打印結果如下:
Object DeserialDATA: 1 song age=0
說明序列化,反序列化正常,B端新增加的int字段被賦予了默認值0.
上面的情況對增加/減少 字段/方法 都適用.
//但當serialVersionUID相同時,它就會將不一樣的field以type的預設值Deserialize,可避開不兼容性問題。