詳解java序列化(二)

http://blog.csdn.net/moreevan/article/details/6698529


好的,繼續爲大家 帶來上一篇文章——詳解java序列化(一)http://blog.csdn.net/moreevan/article/details/6697777中最後第2個問題的解答。

第2個問題:Object是每個類的超類,但是它沒有實現 Serializable接口,但是我們照樣在序列化對象,所以說明一個類要序列化,它的父類不一定要實現Serializable接口。但是在父類中定義 的狀態能被正確 的保存以及讀取嗎?

 

我們還是圍繞上面用過的那些類來做一些修改,看下面這個例子。

Book.java這個類和上次的一樣,不實現Serializable接口

[java] view plaincopy
  1. package kevin.seria;  
  2.   
  3. public class Book{  
  4.     private int isbn;  
  5.       
  6.     public Book(int isbn) {  
  7.         super();  
  8.         this.isbn = isbn;  
  9.     }  
  10.   
  11.     public int getIsbn() {  
  12.         return isbn;  
  13.     }  
  14.   
  15.     public void setIsbn(int isbn) {  
  16.         this.isbn = isbn;  
  17.     }  
  18.   
  19.     @Override  
  20.     public String toString() {  
  21.         return "Book [isbn=" + isbn + "]";  
  22.     }  
  23.       
  24.       
  25. }  

這裏我們新定義一個類NewBook繼承Book類,並且實現 Serializable接口,下面看定義

[java] view plaincopy
  1. package kevin.seria;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class NewBook extends Book implements Serializable{  
  6.     private String author;  
  7.       
  8.     public NewBook(int isbn,String author) {  
  9.         super(isbn);  
  10.         this.author = author;  
  11.     }  
  12.   
  13.     public String getAuthor() {  
  14.         return author;  
  15.     }  
  16.   
  17.     public void setAuthor(String author) {  
  18.         this.author = author;  
  19.     }  
  20.   
  21.     @Override  
  22.     public String toString() {  
  23.         return "NewBook [author=" + author + super.toString()  
  24.                 + "]";  
  25.     }  
  26.   
  27.       
  28. }  

然後,我們把Student類中Book類型的實例變量修改成NewBook類型,修改後的Student類

[java] view plaincopy
  1. package kevin.seria;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. public class Student implements Serializable {  
  6.     private NewBook book;  
  7.     private String name;  
  8.   
  9.     public Student(NewBook book, String name) {  
  10.         super();  
  11.         this.book = book;  
  12.         this.name = name;  
  13.     }  
  14.   
  15.   
  16.     public NewBook getBook() {  
  17.         return book;  
  18.     }  
  19.   
  20.   
  21.     public void setBook(NewBook book) {  
  22.         this.book = book;  
  23.     }  
  24.   
  25.   
  26.     public String getName() {  
  27.         return name;  
  28.     }  
  29.   
  30.     public void setName(String name) {  
  31.         this.name = name;  
  32.     }  
  33.   
  34.     @Override  
  35.     public String toString() {  
  36.         return "Student [book=" + book + ", name=" + name + "]";  
  37.     }  
  38. }  


Simulator類的內容不變,不過我還是把代碼貼出來,可能有的童鞋並沒有看 上一篇文章:

[java] view plaincopy
  1. package kevin.seria;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.io.ObjectInputStream;  
  8. import java.io.ObjectOutputStream;  
  9.   
  10. public class Simulator {  
  11.     public static void main(String[] args) {  
  12.         new Simulator().go();  
  13.     }  
  14.       
  15.     private void go(){  
  16.         Student student = new Student(new NewBook(2011,"moree"),"kevin");  
  17.           
  18.         try {  
  19.             ObjectOutputStream out  = new ObjectOutputStream(new FileOutputStream("seria"));  
  20.             out.writeObject(student); //  
  21.             System.out.println("object has been written..");  
  22.             out.close();  
  23.         } catch (FileNotFoundException e) {  
  24.             e.printStackTrace();  
  25.         } catch (IOException e) {  
  26.             e.printStackTrace();  
  27.         }  
  28.           
  29.         try{  
  30.             ObjectInputStream in = new ObjectInputStream(new FileInputStream("seria"));  
  31.             Student studentRead = (Student) in.readObject();  
  32.             System.out.println("object read here:");  
  33.             System.out.println(studentRead);  
  34.         }catch(FileNotFoundException e){  
  35.             e.printStackTrace();  
  36.         } catch (IOException e) {  
  37.             e.printStackTrace();  
  38.         } catch (ClassNotFoundException e) {  
  39.             // TODO Auto-generated catch block  
  40.             e.printStackTrace();  
  41.         }  
  42.     }  
  43. }  

好,我們運行這個程序 ,看下輸出結果:


從結果可以看出,對象寫成功了,但在讀取的過程中出現了問題,具體的異常原因:no validconstructor,即沒有有效的構造函數,那麼 到底 是哪個 類沒有有效的構造函數呢,到底需要一個什麼樣的構造函數呢?

對於這種情況 ,即父類沒有實現Serializable接口時,但其子類實現 了此接口,那麼 這個子類是可以序列化的,但是在反序列化的過程 中會調用 父類 的無參構造函數,上面異常拋出的原因就是因爲我們在Book類中沒有一個無參的構造函數。好,那我們下面就爲Book類添加一個默認的構造函數。

[java] view plaincopy
  1. package kevin.seria;  
  2.   
  3. public class Book{  
  4.     private int isbn;  
  5.       
  6.     public Book(){  
  7.         isbn=888;  
  8.         System.out.println("Book class no-arg constructor invoked..");  
  9.     }  
  10.     public Book(int isbn) {  
  11.         super();  
  12.         this.isbn = isbn;  
  13.     }  
  14.   
  15.     public int getIsbn() {  
  16.         return isbn;  
  17.     }  
  18.   
  19.     public void setIsbn(int isbn) {  
  20.         this.isbn = isbn;  
  21.     }  
  22.   
  23.     @Override  
  24.     public String toString() {  
  25.         return "Book [isbn=" + isbn + "]";  
  26.     }  
  27.       
  28.       
  29. }  

再來執行一次程序 ,看輸出結果如何:


我們可以看到在反序列化的過程中調用了Book類的無參構造執行一個初始化的操作。

 

好,我們總結一下 :如果父類沒有實現Serializable接口,但其子類實現 了此接口,那麼 這個子類是可以序列化的,但是在反序列化的過程 中會調用 父類 的無參構造函數,所以在其直接父類(注意是直接父類)中必須有一個無參的構造函數。

 

好,對於第2個問題的討論就到這裏,接下來我們提出第3個問題:

如果將一個對象寫入某文件(比如是a),那麼之後對這個對象進行一些修改,然後把修改的對象再寫入文件a,那麼文件a中會包含該對象的兩個 版本嗎?

至於包不包含,我們代碼見分曉:

我們修改Simulator類如下:

[java] view plaincopy
  1. package kevin.seria;  
  2.   
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.FileOutputStream;  
  6. import java.io.IOException;  
  7. import java.io.ObjectInputStream;  
  8. import java.io.ObjectOutputStream;  
  9.   
  10. public class Simulator {  
  11.     public static void main(String[] args) {  
  12.         new Simulator().go();  
  13.     }  
  14.       
  15.     private void go(){  
  16.         try {  
  17.             ObjectOutputStream out  = new ObjectOutputStream(new FileOutputStream("seria"));  
  18.             Student student1 = new Student(new NewBook(2011,"moree"),"kevin");  
  19.             out.writeObject(student1); //  
  20.             student1.setName("Jordan");  
  21.             out.writeObject(student1);  
  22.             student1.setName("Paul");  
  23.             out.writeObject(student1);  
  24.             System.out.println("object has been written..");  
  25.             out.close();  
  26.         } catch (FileNotFoundException e) {  
  27.             e.printStackTrace();  
  28.         } catch (IOException e) {  
  29.             e.printStackTrace();  
  30.         }  
  31.           
  32.         try{  
  33.             ObjectInputStream in = new ObjectInputStream(new FileInputStream("seria"));  
  34.             Student s1 = (Student)in.readObject();  
  35.             Student s2 = (Student)in.readObject();  
  36.             Student s3 = (Student)in.readObject();  
  37.             System.out.println("Objects read here: ");  
  38.             System.out.println("Student1's name: "+s1.getName());  
  39.             System.out.println("Student2's name: "+s2.getName());  
  40.             System.out.println("Student3's name: "+s3.getName());  
  41.         }catch(FileNotFoundException e){  
  42.             e.printStackTrace();  
  43.         } catch (IOException e) {  
  44.             e.printStackTrace();  
  45.         } catch (ClassNotFoundException e) {  
  46.             // TODO Auto-generated catch block  
  47.             e.printStackTrace();  
  48.         }  
  49.     }  
  50. }  

我們希望輸出三個人的名字:kevin,Jordan,Paul,那看一下它到底是不是如我們所願呢:


事與願違啊,它輸出了三個kevin,這證明 我們對student名字的修改並沒有被寫入。原因是序列化輸出過程跟蹤寫入流的對象,試圖將同一個對象寫入流時,不會導致該對象被複製,而只是將一個句柄寫入流,該句柄指向流中相同對象的第一個對象出現的位置。

那我們如何來避免這種情況 ,讓它輸出三個人名呢,方法是在writeObject()之前調用out.reset()方法,這個方法的作用是清除流中保存的寫入對象的記錄。我們還是通過代碼來看下效果。

[java] view plaincopy
  1. try {  
  2.             ObjectOutputStream out  = new ObjectOutputStream(new FileOutputStream("seria"));  
  3.             Student student1 = new Student(new NewBook(2011,"moree"),"kevin");  
  4.             out.writeObject(student1); //  
  5.             out.reset();  
  6.             student1.setName("Jordan");  
  7.             out.writeObject(student1);  
  8.             out.reset();  
  9.             student1.setName("Paul");  
  10.             out.writeObject(student1);  
  11.             System.out.println("object has been written..");  
  12.             out.close();  
  13.         } catch (FileNotFoundException e) {  

這樣修改以後就會輸出我們期望的結果了。

好,第3個問題的討論到此爲止,如果想深入瞭解java序列化,可以看下專門討論這方面的書。

8/18/2011 by Kevin

(End)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章