zoukankan      html  css  js  c++  java
  • Java 之 I/O 系列 02 ——序列化(二)

    Java 之 I/O 系列 目录

    Java 之 I/O 系列 01 ——基础

    Java 之 I/O 系列 02 ——序列化(一) 

    Java 之 I/O 系列 02 ——序列化(二)

     继续上篇的第二个问题

    如果一个类实现了Serializable接口,但是它的父类没有实现 ,这个类可不可以序列化?

    Object是每个类的超类,但是它没有实现 Serializable接口,但是我们照样在序列化对象,所以说明一个类要序列化,它的父类不一定要实现Serializable接口。但是在父类中定义 的状态能被正确 的保存以及读取吗?

    还是围绕上面用过的那些类来做一些修改,看下面这个例子。

    Book.java这个类和上次的一样,不实现Serializable接口

     1 public class Book {
     2 
     3     private int isbn;
     4     public Book(int isbn){
     5         this.isbn = isbn;
     6     }
     7     public int getIsbn() {
     8         return isbn;
     9     }
    10     public void setIsbn(int isbn) {
    11         this.isbn = isbn;
    12     }
    13     
    14     public String toString(){
    15         return "Book [isbn = "+isbn+"]";
    16     }
    17     
    18 }

    这里新定义一个类NewBook继承Book类,并且实现 Serializable接口,下面看定义

     1 ublic class NewBook extends Book implements Serializable {
     2 
     3     private String author;
     4 
     5     public NewBook(int isbn, String author) {
     6         super(isbn);
     7         this.author = author;
     8     }
     9 
    10     public String getAuthor() {
    11         return author;
    12     }
    13 
    14     public void setAuthor(String author) {
    15         this.author = author;
    16     }
    17 
    18     @Override
    19     public String toString() {
    20         return "NewBook [author=" + author + super.toString() + "]";
    21     }
    22 }

    然后,把Student类中Book类型的实例变量修改成NewBook类型,修改后的Student类

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

    Simulator类的内容不变

     1 public class Simulator {
     2     public static void main(String[] args) {
     3         new Simulator().go();
     4     }
     5 
     6     private void go() {
     7         Student student = new Student(new NewBook(2014,"author11"), "xingle");
     8         try {
     9             ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test"));
    10             out.writeObject(student);
    11             //System.out.println(System.currentTimeMillis());
    12             System.out.println("object has been written  ");
    13             out.close();
    14         } catch (FileNotFoundException e) {
    15             e.printStackTrace();
    16         } catch (IOException e) {
    17             e.printStackTrace();
    18         }
    19         
    20         try {
    21             ObjectInputStream in = new ObjectInputStream(new FileInputStream("test"));
    22             Student stuRead = (Student) in.readObject();
    23             System.out.println("object read here");
    24             System.out.println(stuRead);
    25         } catch (FileNotFoundException e) {
    26             e.printStackTrace();
    27         } catch (IOException e) {
    28             e.printStackTrace();
    29         } catch (ClassNotFoundException e) {
    30             e.printStackTrace();
    31         }
    32 
    33     }
    34 
    35 }

    运行这个程序 ,看下输出结果:

     

    从结果可以看出,对象写成功了,但在读取的过程中出现了问题,具体的异常原因:no validconstructor,即没有有效的构造函数,那么 到底 是哪个 类没有有效的构造函数呢,到底需要一个什么样的构造函数呢?

    对于这种情况 ,即父类没有实现Serializable接口时,但其子类实现 了此接口,那么 这个子类是可以序列化的,但是在反序列化的过程 中会调用 父类 的无参构造函数,上面异常抛出的原因就是因为我们在Book类中没有一个无参的构造函数。好,那我们下面就为Book类添加一个默认的构造函数。

     1 public class Book {
     2 
     3     private int isbn;
     4     //增加默认构造函数
     5     public Book(){
     6         isbn = 100;
     7         System.out.println("Book class no-arg constructor invoked..");
     8     }
     9     public Book(int isbn){
    10         this.isbn = isbn;
    11     }
    12     public int getIsbn() {
    13         return isbn;
    14     }
    15     public void setIsbn(int isbn) {
    16         this.isbn = isbn;
    17     }
    18     
    19     public String toString(){
    20         return "Book [isbn = "+isbn+"]";
    21     }
    22     
    23 }

    再来执行一次程序 ,看输出结果如何:

     可以看到在反序列化的过程中调用了Book类的无参构造执行一个初始化的操作。

     总结一下 :如果父类没有实现Serializable接口,但其子类实现 了此接口,那么 这个子类是可以序列化的,但是在反序列化的过程 中会调用 父类 的无参构造函数,所以在其直接父类(注意是直接父类)中必须有一个无参的构造函数。

    对于第2个问题的讨论就到这里,接下来我们提出第3个问题:

    如果将一个对象写入某文件(比如是a),那么之后对这个对象进行一些修改,然后把修改的对象再写入文件a,那么文件a中会包含该对象的两个版本吗?

    修改Simulator类如下:

     1 public class Simulator {
     2     public static void main(String[] args) {
     3         new Simulator().go();
     4     }
     5 
     6     private void go() {
     7 
     8         try {
     9             ObjectOutputStream out = new ObjectOutputStream(
    10                     new FileOutputStream("test"));
    11             Student student = new Student(new NewBook(2014, "testAuthor"),"hehe");                
    12             out.writeObject(student);
    13             student.setName("haha");
    14             out.writeObject(student);
    15             student.setName("xixi");
    16             out.writeObject(student);
    17             System.out.println("object has been written  ");
    18             out.close();
    19         } catch (FileNotFoundException e) {
    20             e.printStackTrace();
    21         } catch (IOException e) {
    22             e.printStackTrace();
    23         }
    24 
    25         try {
    26             ObjectInputStream in = new ObjectInputStream(new FileInputStream(
    27                     "test"));
    28             Student student1 = (Student) in.readObject();
    29             Student student2 = (Student) in.readObject();
    30             Student student3 = (Student) in.readObject();
    31             System.out.println("object read here");
    32             System.out.println("Student 1 name :"+student1.getName());
    33             System.out.println("Student 2 name :"+student2.getName());
    34             System.out.println("Student 3 name :"+student3.getName());
    35         } catch (FileNotFoundException e) {
    36             e.printStackTrace();
    37         } catch (IOException e) {
    38             e.printStackTrace();
    39         } catch (ClassNotFoundException e) {
    40             e.printStackTrace();
    41         }
    42     }
    43 }

      执行结果:

    object has been written
    Book class no-arg constructor invoked..
    object read here
    Student 1 name :hehe
    Student 2 name :hehe
    Student 3 name :hehe

    它输出了三个hehe,这证明 我们对student名字的修改并没有被写入。原因是序列化输出过程跟踪写入流的对象,试图将同一个对象写入流时,不会导致该对象被复制,而只是将一个句柄写入流,该句柄指向流中相同对象的第一个对象出现的位置。

    那我们如何来避免这种情况 ,让它输出三个人名呢,方法是在writeObject()之前调用out.reset()方法,这个方法的作用是清除流中保存的写入对象的记录。

     1 public class Simulator {
     2     public static void main(String[] args) {
     3         new Simulator().go();
     4     }
     5 
     6     private void go() {
     7 
     8         try {
     9             ObjectOutputStream out = new ObjectOutputStream(
    10                     new FileOutputStream("test"));
    11             Student student = new Student(new NewBook(2014, "testAuthor"),"hehe");                
    12             out.writeObject(student);
    13             //Reset will disregard the state of any objects already written to the stream. 
    14             //The state is reset to be the same as a new ObjectOutputStream
    15             out.reset();
    16             student.setName("haha");
    17             out.reset();
    18             out.writeObject(student);
    19             student.setName("xixi");
    20             out.reset();
    21             out.writeObject(student);
    22             System.out.println("object has been written  ");
    23             out.close();
    24         } catch (FileNotFoundException e) {
    25             e.printStackTrace();
    26         } catch (IOException e) {
    27             e.printStackTrace();
    28         }
    29 
    30         try {
    31             ObjectInputStream in = new ObjectInputStream(new FileInputStream(
    32                     "test"));
    33             Student student1 = (Student) in.readObject();
    34             Student student2 = (Student) in.readObject();
    35             Student student3 = (Student) in.readObject();
    36             System.out.println("object read here");
    37             System.out.println("Student 1 name :"+student1.getName());
    38             System.out.println("Student 2 name :"+student2.getName());
    39             System.out.println("Student 3 name :"+student3.getName());
    40         } catch (FileNotFoundException e) {
    41             e.printStackTrace();
    42         } catch (IOException e) {
    43             e.printStackTrace();
    44         } catch (ClassNotFoundException e) {
    45             e.printStackTrace();
    46         }
    47     }
    48 }

    执行结果:

    object has been written
    Book class no-arg constructor invoked..
    Book class no-arg constructor invoked..
    Book class no-arg constructor invoked..
    object read here
    Student 1 name :hehe
    Student 2 name :haha
    Student 3 name :xixi

    这样修改以后就会输出我们期望的结果了。

    好,第3个问题的讨论到此为止,如果想深入了解java序列化,可以看下专门讨论这方面的书。

  • 相关阅读:
    AutoCAD.NET 二次开发(一) 自定义菜单及自动加载
    WSS 3.0部署备忘 一
    WSS 3.0部署备忘 四
    WSS 3.0部署备忘 三
    WSS 3.0部署备忘 二
    loj_1042
    loj_1045
    vim的学习笔记(3)
    Linux的磁盘与文件管理系统(1)
    文件与文件系统的压缩与打包
  • 原文地址:https://www.cnblogs.com/xingele0917/p/3811431.html
Copyright © 2011-2022 走看看