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接口
- package kevin.seria;
- public class Book{
- private int isbn;
- public Book(int isbn) {
- super();
- this.isbn = isbn;
- }
- public int getIsbn() {
- return isbn;
- }
- public void setIsbn(int isbn) {
- this.isbn = isbn;
- }
- @Override
- public String toString() {
- return "Book [isbn=" + isbn + "]";
- }
- }
這裏我們新定義一個類NewBook繼承Book類,並且實現 Serializable接口,下面看定義
- package kevin.seria;
- import java.io.Serializable;
- public class NewBook extends Book implements Serializable{
- private String author;
- public NewBook(int isbn,String author) {
- super(isbn);
- this.author = author;
- }
- public String getAuthor() {
- return author;
- }
- public void setAuthor(String author) {
- this.author = author;
- }
- @Override
- public String toString() {
- return "NewBook [author=" + author + super.toString()
- + "]";
- }
- }
然後,我們把Student類中Book類型的實例變量修改成NewBook類型,修改後的Student類
- package kevin.seria;
- import java.io.Serializable;
- public class Student implements Serializable {
- private NewBook book;
- private String name;
- public Student(NewBook book, String name) {
- super();
- this.book = book;
- this.name = name;
- }
- public NewBook getBook() {
- return book;
- }
- public void setBook(NewBook book) {
- this.book = book;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public String toString() {
- return "Student [book=" + book + ", name=" + name + "]";
- }
- }
Simulator類的內容不變,不過我還是把代碼貼出來,可能有的童鞋並沒有看 上一篇文章:
- package kevin.seria;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- public class Simulator {
- public static void main(String[] args) {
- new Simulator().go();
- }
- private void go(){
- Student student = new Student(new NewBook(2011,"moree"),"kevin");
- try {
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("seria"));
- out.writeObject(student); //
- System.out.println("object has been written..");
- out.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try{
- ObjectInputStream in = new ObjectInputStream(new FileInputStream("seria"));
- Student studentRead = (Student) in.readObject();
- System.out.println("object read here:");
- System.out.println(studentRead);
- }catch(FileNotFoundException e){
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
好,我們運行這個程序 ,看下輸出結果:
從結果可以看出,對象寫成功了,但在讀取的過程中出現了問題,具體的異常原因:no validconstructor,即沒有有效的構造函數,那麼 到底 是哪個 類沒有有效的構造函數呢,到底需要一個什麼樣的構造函數呢?
對於這種情況 ,即父類沒有實現Serializable接口時,但其子類實現 了此接口,那麼 這個子類是可以序列化的,但是在反序列化的過程 中會調用 父類 的無參構造函數,上面異常拋出的原因就是因爲我們在Book類中沒有一個無參的構造函數。好,那我們下面就爲Book類添加一個默認的構造函數。
- package kevin.seria;
- public class Book{
- private int isbn;
- public Book(){
- isbn=888;
- System.out.println("Book class no-arg constructor invoked..");
- }
- public Book(int isbn) {
- super();
- this.isbn = isbn;
- }
- public int getIsbn() {
- return isbn;
- }
- public void setIsbn(int isbn) {
- this.isbn = isbn;
- }
- @Override
- public String toString() {
- return "Book [isbn=" + isbn + "]";
- }
- }
再來執行一次程序 ,看輸出結果如何:
我們可以看到在反序列化的過程中調用了Book類的無參構造執行一個初始化的操作。
好,我們總結一下 :如果父類沒有實現Serializable接口,但其子類實現 了此接口,那麼 這個子類是可以序列化的,但是在反序列化的過程 中會調用 父類 的無參構造函數,所以在其直接父類(注意是直接父類)中必須有一個無參的構造函數。
好,對於第2個問題的討論就到這裏,接下來我們提出第3個問題:
如果將一個對象寫入某文件(比如是a),那麼之後對這個對象進行一些修改,然後把修改的對象再寫入文件a,那麼文件a中會包含該對象的兩個 版本嗎?
至於包不包含,我們代碼見分曉:
我們修改Simulator類如下:
- package kevin.seria;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- public class Simulator {
- public static void main(String[] args) {
- new Simulator().go();
- }
- private void go(){
- try {
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("seria"));
- Student student1 = new Student(new NewBook(2011,"moree"),"kevin");
- out.writeObject(student1); //
- student1.setName("Jordan");
- out.writeObject(student1);
- student1.setName("Paul");
- out.writeObject(student1);
- System.out.println("object has been written..");
- out.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- try{
- ObjectInputStream in = new ObjectInputStream(new FileInputStream("seria"));
- Student s1 = (Student)in.readObject();
- Student s2 = (Student)in.readObject();
- Student s3 = (Student)in.readObject();
- System.out.println("Objects read here: ");
- System.out.println("Student1's name: "+s1.getName());
- System.out.println("Student2's name: "+s2.getName());
- System.out.println("Student3's name: "+s3.getName());
- }catch(FileNotFoundException e){
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
我們希望輸出三個人的名字:kevin,Jordan,Paul,那看一下它到底是不是如我們所願呢:
事與願違啊,它輸出了三個kevin,這證明 我們對student名字的修改並沒有被寫入。原因是序列化輸出過程跟蹤寫入流的對象,試圖將同一個對象寫入流時,不會導致該對象被複製,而只是將一個句柄寫入流,該句柄指向流中相同對象的第一個對象出現的位置。
那我們如何來避免這種情況 ,讓它輸出三個人名呢,方法是在writeObject()之前調用out.reset()方法,這個方法的作用是清除流中保存的寫入對象的記錄。我們還是通過代碼來看下效果。
- try {
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("seria"));
- Student student1 = new Student(new NewBook(2011,"moree"),"kevin");
- out.writeObject(student1); //
- out.reset();
- student1.setName("Jordan");
- out.writeObject(student1);
- out.reset();
- student1.setName("Paul");
- out.writeObject(student1);
- System.out.println("object has been written..");
- out.close();
- } catch (FileNotFoundException e) {
這樣修改以後就會輸出我們期望的結果了。
好,第3個問題的討論到此爲止,如果想深入瞭解java序列化,可以看下專門討論這方面的書。
8/18/2011 by Kevin
(End)