Java的物件序列化是一個輕量級的持久化過程,序列化時,可以將java物件以位元組序列形式寫入硬碟、資料庫或者通過網路傳輸到另一個JVM等等,反序列化是讀取序列化的檔案,將其在JVM中還原為java物件的過程。

1.簡單的序列化和反序列化:

最簡單的序列化是物件實現Serializable序列化介面,這個介面是一個標記介面,不需要實現任何方法。

下面的小例子簡單展示java物件序列化和反序列化的過程:

  1. import java.io.*;  
  2. //簡單的測試物件
  3. publicclass TestObject implements Serializable{}  
  4. import java.io.*;  
  5. //序列化
  6. publicclass SerializeObject{  
  7.     publicstaticvoid main(String[] args)throws Exception{  
  8.         ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“x.file”));  
  9.         TestObject obj = new TestObject();  
  10.         //將TestObject物件序列化到x.file檔案中
  11.         out.write(obj);  
  12. }  
  13. }  
  14. import java.io.*;  
  15. //反序列化
  16. publicclass DeserializeObject{  
  17.     publicstaticvoid main(String[] args)throw Exception{  
  18.         ObjectInputStream in = new ObjectInputStream(new FileInputStream(“x.file”));  
  19.         Object myObj = in.readObject();  
  20.         System.out.println(myObj.getClass());  
  21. }  
  22. }  

輸出結果:

class TestObject

在序列化時,使用ObjectOutputStream將物件序列化寫出,反序列化時,使用ObjectInputStream將物件反序列化讀入。

注意:反序列化物件使用時,物件的class檔案必須在classpath中,否則,JVM找不到物件的class檔案會丟擲ClassNotFoundException。

2.序列化控制:

預設的Serializable序列化是將物件整體序列化,但是對於一些特殊的需求例如:序列化部分物件或者反序列化部分物件的情況,就必須使用Externalizable介面來代替Serializable介面,Externalizable的writeExternal()和readExternal()方法可以實現對序列化的控制,這兩個方法在物件序列化和反序列化時自動呼叫,例子如下:

  1. import java.io.*;  
  2. class Blip1 implements Externalizable{  
  3.     public Blip1(){  
  4.         System.out.println(“Blip1 Constructor”);  
  5. }  
  6. publicvoid writeExternal(ObjectOutput out)throws IOException{  
  7.     System.out.println(“Blip1.writeExternal”);  
  8. }  
  9. publicvoid readExternal(ObjectInput in) throws IOException, ClassNotFoundException{  
  10.     System.out.println(Blip1.readExternal);  
  11. }  
  12. }  
  13. class Blip2 implements Externalizable{  
  14.     Blip1(){  
  15.         System.out.println(“Blip2 Constructor”);  
  16. }  
  17. publicvoid writeExternal(ObjectOutput out)throws IOException{  
  18.     System.out.println(“Blip2.writeExternal”);  
  19. }  
  20. publicvoid readExternal(ObjectInput in) throws IOException, ClassNotFoundException{  
  21.     System.out.println(Blip2.readExternal);  
  22. }  
  23. }  
  24. publicclass Blips{  
  25.     publicstaticvoid main(String[] args)throws IOException, ClassNotFoundException{  
  26.         System.out.println(“Constructing objects:”);  
  27.         Blip1 b1 = new Blip1();  
  28.         Blip2 b2 = new Blip2();  
  29.         //序列化
  30.         ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(“Blips.out”));  
  31.         System.out.println(“Saving objects:”);  
  32.         o.writeObject(b1);  
  33.         o.writeObject(b2);  
  34.         //反序列化
  35.         ObjectInputStream in = new ObjectInputStream(new FileInputStream(“Blips.out”));  
  36.         System.out.println(“Recovering b1:”);  
  37.         b1 = (Blip1)in.readObject();  
  38.         //由於Blip2的預設無引數構造方法不是public的,所以會拋異常
  39.         //System.out.println(“Recovering b2:”);
  40.         //b2 = (Blip2)in.readObject();
  41. }  
  42. }  

輸出結果:

Constructing objects:

Blip1 Constructor

Blip2 Constructor

Saving objects:

Blip1.writeExternal

Blip2.writeExternal

Recovering b1:

Blip1 Constructor

Blip1.readExternal

注意:由於物件在反序列化時需要呼叫預設的public的無引數構造方法建立物件,所以Blip2非public型別無引數構造方法無法反序列化。所以使用Externalizable序列化和反序列化時,物件必須要有public型別的無引數構造方法,這一點是Externalizable和Serializable的區別。

Serializable和Externlizable反序列化的區別:

(1).Serializable的反序列化是通過序列化的位元組陣列序列進行反序列化的。物件的序列化的反序列化都是自動進行的。

(2).Externalizable的反序列化是通過呼叫預設的構造方法進行的。物件的序列化需要使用writeExternal()顯式控制,物件的反序列化需要使用readExternal()顯式控制,不是自動進行的。

3. Externlizable序列化高階:

使用Externlizable序列化和反序列化物件時,readExternal()和writeExternal()方法可以物件中指定欄位進行初始化,例子如下:

  1. import java.io.*;  
  2. publicclass Test implements Externalizable{  
  3.     privateint i;  
  4.     private String s;  
  5.     public Test(){  
  6.         System.out.println(“Test Constructor”);  
  7. }  
  8. public Test(String x, int a){  
  9.     System.out.println(“Test(String x, int a)”);  
  10.     i = a;  
  11.     s = x;  
  12. }  
  13. publicvoid toString(){  
  14.     return s + i;  
  15. }  
  16. publicvoid writeExternal(ObjectOutput out)throws IOException{  
  17.     System.out.println(“Test.writeExternal”);  
  18.     out.writeObject(s);  
  19.     out.writeInt(i);  
  20. }  
  21. publicvoid readExternal(ObjectInput in)throws IOException, ClassNotFoundException{  
  22.     System.out.println(“Test.readExternal”);  
  23.     s = (String)in.readObject();  
  24.     i = in.readInt();  
  25. }  
  26. publicstaticvoid main()throws IOException, ClassNotFoundException{  
  27.     System.out.println(“Constructing objects:”);  
  28.     Test t1 = new Test(“A String”, 47);  
  29.     Test t2 = new Test();  
  30.     System.out.println(t1);  
  31.     System.out.println(t2);  
  32.     //序列化
  33.     ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream(“Test.out”));  
  34.     System.out.println(“Saving objects:”);  
  35.     o.writeObject(t1);\  
  36.     o.writeObject(t2);  
  37.     o.close();  
  38.     //反序列化
  39.     ObjectInputStream in = new ObjectInputStream(new FileInputStream(“Test.out”));  
  40.     System.out.println(“Recovering objects:”);  
  41.     t1 = (Test)in.readObject();  
  42.     System.out.println(t1);  
  43.     t2 = (Test)in.readObject();  
  44.     System.out.println(t2);  
  45. }  
  46. }  

輸出結果:

Constructing objects:

Test(String x, int a)

A String 47

Test Constructor

null0

Saving objects:

Test.writeExternal

Test.writeExternal

Recovering objects:

Test Constructor

Test.readExternal

A String 47

Test Constructor

Test.readExternal

null0

該例子中,當建立物件使用非無引數構造方法時,物件欄位被初始化,因此序列化時可以將初始化的值儲存。當建立物件使用無引數的構造方法時,物件的欄位沒有被賦值而使用預設的初始化值。反序列化時只會呼叫預設的無引數構造方法。

因此,為了使序列化和反序列化更高效使用,在序列化時,呼叫writeExternal()只寫有用的重要資料,而在反序列化時,readObject()只讀取有用的重要資料。

4.transient關鍵字:

當物件實現Serializable介面進行自動序列化時,類中某些欄位不想被序列化,需要使用transient關鍵字,雖然Externalizable通過writeExternal()方法也可以實現此功能,但是序列化不是自動進行的,使用Serializable和transient關鍵字更加方便。

例子如下:

  1. import java.io.*;  
  2. publicclass User implements Serializable{  
  3.     private String username;  
  4.     privatetransient String password;  
  5.     public User(String name, String pwd){  
  6.         username = name;  
  7.         password = pwd;  
  8. }  
  9. public String toString(){  
  10.     return “username:” + username + “, password:” + password;  
  11. }  
  12. publicstaticvoid main(String[] args)throws Exception{  
  13.     User user =