好的,继续为大家 带来上一篇文章——详解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) {
-
- 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) {
-
- 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序列化,可以看下专门讨论这方面的书。