Java 之 I/O 系列 目录

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

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

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

继续上篇的第二个问题

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

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

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

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

 public class Book {

     private int isbn;
public Book(int isbn){
this.isbn = isbn;
}
public int getIsbn() {
return isbn;
}
public void setIsbn(int isbn) {
this.isbn = isbn;
} public String toString(){
return "Book [isbn = "+isbn+"]";
} }

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

 ublic 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类

 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;
} public String toString() {
return "Student [book=" + book + ", name=" + name + "]";
} }

Simulator类的内容不变

 public class Simulator {
public static void main(String[] args) {
new Simulator().go();
} private void go() {
Student student = new Student(new NewBook(2014,"author11"), "xingle");
try {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("test"));
out.writeObject(student);
//System.out.println(System.currentTimeMillis());
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("test"));
Student stuRead = (Student) in.readObject();
System.out.println("object read here");
System.out.println(stuRead);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} } }

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

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

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

 public class Book {

     private int isbn;
//增加默认构造函数
public Book(){
isbn = 100;
System.out.println("Book class no-arg constructor invoked..");
}
public Book(int isbn){
this.isbn = isbn;
}
public int getIsbn() {
return isbn;
}
public void setIsbn(int isbn) {
this.isbn = isbn;
} public String toString(){
return "Book [isbn = "+isbn+"]";
} }

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

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

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

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

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

修改Simulator类如下:

 public class Simulator {
public static void main(String[] args) {
new Simulator().go();
} private void go() { try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("test"));
Student student = new Student(new NewBook(2014, "testAuthor"),"hehe");
out.writeObject(student);
student.setName("haha");
out.writeObject(student);
student.setName("xixi");
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(
"test"));
Student student1 = (Student) in.readObject();
Student student2 = (Student) in.readObject();
Student student3 = (Student) in.readObject();
System.out.println("object read here");
System.out.println("Student 1 name :"+student1.getName());
System.out.println("Student 2 name :"+student2.getName());
System.out.println("Student 3 name :"+student3.getName());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

执行结果:

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()方法,这个方法的作用是清除流中保存的写入对象的记录。

 public class Simulator {
public static void main(String[] args) {
new Simulator().go();
} private void go() { try {
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("test"));
Student student = new Student(new NewBook(2014, "testAuthor"),"hehe");
out.writeObject(student);
//Reset will disregard the state of any objects already written to the stream.
//The state is reset to be the same as a new ObjectOutputStream
out.reset();
student.setName("haha");
out.reset();
out.writeObject(student);
student.setName("xixi");
out.reset();
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(
"test"));
Student student1 = (Student) in.readObject();
Student student2 = (Student) in.readObject();
Student student3 = (Student) in.readObject();
System.out.println("object read here");
System.out.println("Student 1 name :"+student1.getName());
System.out.println("Student 2 name :"+student2.getName());
System.out.println("Student 3 name :"+student3.getName());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}

执行结果:

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序列化,可以看下专门讨论这方面的书。

Java 之 I/O 系列 02 ——序列化(二)的更多相关文章

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

    Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 一 序列化概述 序 ...

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

    Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 整理<疯狂j ...

  3. Java 集合系列 02 Collection架构

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  4. java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)

    我们以ByteArrayInputStream,拉开对字节类型的“输入流”的学习序幕.本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的 ...

  5. Java NIO系列教程(二) Channel通道介绍及FileChannel详解

    目录: <Java NIO系列教程(二) Channel> <Java NIO系列教程(三) Channel之Socket通道> Channel是一个通道,可以通过它读取和写入 ...

  6. Java 设计模式系列(十二)策略模式(Strategy)

    Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...

  7. Java练习 SDUT-2733_小鑫の日常系列故事(二)——石头剪子布

    小鑫の日常系列故事(二)--石头剪子布 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 小鑫在上幼儿园的时候,喜欢跟小伙 ...

  8. 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念

    深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...

  9. Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析

    原文地址:http://www.javatang.com 一个典型的thread dump文件主要由一下几个部分组成: 上图将JVM上的线程堆栈信息和线程信息做了详细的拆解. 第一部分:Full th ...

随机推荐

  1. Writing On-Error Trigger In Oracle Forms

    Suppose you want to handle an error in oracle forms and want to display custom error message for tha ...

  2. Codeforces Round #259 (Div. 2)AB

    链接:http://codeforces.com/contest/454/problem/A A. Little Pony and Crystal Mine time limit per test 1 ...

  3. 最大后验估计 -- Maximum-a-Posteriori (MAP) Estimation

    最大后验估计是根据经验数据获得对难以观察的量的点估计.与最大似然估计类似,但是最大的不同时,最大后验估计的融入了要估计量的先验分布在其中.故最大后验估计可以看做规则化的最大似然估计.

  4. STORM_0008_Structure-of-the-codebase_Storm的代码库的结构

    http://storm.apache.org/releases/1.0.1/Structure-of-the-codebase.html Structure of the codebase 源码分成 ...

  5. iOS问题处理:如何在Mac下显示Finder中的所有文件

    摘自:http://www.cnblogs.com/elfsundae/archive/2010/11/30/1892544.html 在Unix下工作,你可能需要处理一些“特殊“文件或文件夹,例如/ ...

  6. C# 线程(二):关于线程的相关概念

    From : http://kb.cnblogs.com/page/42528/ 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源. 而一个进程又 ...

  7. Protractor AngularJS测试框架教程

    Protractor是一个建立在WebDriverJS基础上的端到端(E2E)的AngularJS JavaScript Web应用程序测试框架.Protractor全自动化真实的模拟用户在真正的浏览 ...

  8. Spring XML配置实现AOP

    1:  首先我们要定义 配置成切面的类 package cn.gbx.example; import org.aspectj.lang.ProceedingJoinPoint; import org. ...

  9. html 如何获取表格中所选行的一行数据,并赋值到对应的TEXT里面?

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. Docker-数据卷和数据容器卷

    容器中管理数据主要有两种方式: 数据卷(Data Volumes) 数据卷容器(Data Volumes Dontainers) 数据卷 使用-v可以挂载一个本地的目录到容器中作为数据卷. [root ...