什麼是序列化? 我們創建的對象只有在Java虛擬機保持運行時,才會存在於記憶體中。如果想要超出Java虛擬機的生命周期,就可以將對象序列化,將對象狀態轉換為位元組序列,寫入文件(或socket傳輸),後面使用時再讀入文件,讀入原始位元組並創建一個完全相同的對象。 PS:只有對象的狀態會被序列化,類本身或方 ...
什麼是序列化?
我們創建的對象只有在Java虛擬機保持運行時,才會存在於記憶體中。如果想要超出Java虛擬機的生命周期,就可以將對象序列化,將對象狀態轉換為位元組序列,寫入文件(或socket傳輸),後面使用時再讀入文件,讀入原始位元組並創建一個完全相同的對象。
PS:只有對象的狀態會被序列化,類本身或方法都不會被序列化。
三種序列化方式
1、預設機制
需要序列化的對象,實現java.io.Serializable介面即可。
例子:
import java.io.Serializable; import java.util.Date; import java.util.Calendar; public class PersistentTime implements Serializable { private Date time; public PersistentTime() { time = Calendar.getInstance().getTime(); } public Date getTime() { return time; } } import java.io.ObjectOutputStream; import java.io.FileOutputStream; import java.io.IOException; public class FlattenTime { public static void main(String[] args) { String filename = "time.ser"; if (args.length > 0) { filename = args[0]; } PersistentTime time = new PersistentTime(); FileOutputStream fos = null; ObjectOutputStream out = null; try { fos = new FileOutputStream(filename); out = new ObjectOutputStream(fos); out.writeObject(time); out.close(); } catch (IOException ex) { ex.printStackTrace(); } } } import java.io.ObjectInputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.Calendar; public class InflateTime { public static void main(String[] args) { String filename = "time.ser"; if (args.length > 0) { filename = args[0]; } PersistentTime time = null; FileInputStream fis = null; ObjectInputStream in = null; try { fis = new FileInputStream(filename); in = new ObjectInputStream(fis); time = (PersistentTime) in.readObject(); in.close(); } catch (IOException ex) { ex.printStackTrace(); } catch (ClassNotFoundException ex) { ex.printStackTrace(); } // print out restored time System.out.println("Flattened time: " + time.getTime()); System.out.println(); // print out the current time System.out.println("Current time: " + Calendar.getInstance().getTime()); } }View Code
註意:
1、持久化的對象必現實現Serializable介面或繼承的父類實現了。
2、不能(例如系統級別的類Thread、OutPutStream)或不想被序列化的欄位必需加transient聲明。
transient是欄位的修飾符,當一個欄位聲明為transient時,它就不能被序列化。
transient不能聲明方法、類、介面,它們不需要被序列化。
2、自定義預設協議
由於類的構造函數只要在對象創建時候才調用,而反序列化的對象是用readObject創建的,不會再去調構造函數。如果構造函數里有執行某些行為,那麼反序列化回來的對象就會丟失這些行為。
解決方法:增加兩個方法,在保證序列化的預設行為後,增加自己要的行為。
private void writeObject(ObjectOutputStream out) throws IOException; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
例子:
import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class PersistentAnimation implements Serializable, Runnable { private static final long serialVersionUID = 2850527457134768151L; transient private Thread animator; private int animationSpeed; public PersistentAnimation(int animationSpeed) { this.animationSpeed = animationSpeed; startAnimation(); } public void run() { while (true) { // do animation here } } private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // our "pseudo-constructor" in.defaultReadObject(); // now we are a "live" object again, so let's run rebuild and start startAnimation(); } private void startAnimation() { animator = new Thread(this); animator.start(); } }View Code
同理,如果父類已經實現Serializable介面,子類又不想被序列化,就可以添加這兩個內容為空的方法。
3、自定義協議
實現Externalizable介面,覆寫writeExternal、readExternal兩個方法,自己定義序列化協議(例如pdf的存取)。
public void writeExternal(ObjectOutput out) throws IOException; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
序列化的陷阱
1、緩存流中的對象,再修改對象狀態再寫入是無效的。
ObjectOutputStream out = new ObjectOutputStream(...); MyObject obj = new MyObject(); // must be Serializable obj.setState(100); out.writeObject(obj); // saves object with state = 100 obj.setState(200); out.writeObject(obj); // does not save new object state
2、版本控制
當你序列化一個對象存儲到文件後,修改類的定義(例如增加個欄位),當你反序列化時,會報一個java.io.InvalidClassException的異常。這是因為所有可以持續化的類都會有一個唯一的標識符,如果類的標識符跟反序列化對象的標識符不一樣時,就會報這個異常。
解決方法:增加一個serialVersionUID欄位作為類的標識符,只要標識符不變,就可反序列化。
添加方法:在eclipse中,滑鼠懸浮到類名上,就可以看到“添加已生成的串列版本標識”,點擊即可添加serialVersionUID。
3、性能問題
序列化對象寫入文件的性能會比自己手動寫入文件低。
參考文獻
http://www.oracle.com/technetwork/articles/java/javaserial-1536170.html