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. CurlSharp

    https://github.com/masroore/CurlSharp clone版本库之后,在本地使用,会遇到找不到dll的情况 编译EasyGet项目之后,进行调试,会提示 System.Ba ...

  2. struts2--表单标签

    struts2的表单标签可分为两类:form标签本身和包装HTML表单元素的其他标签.form标签本身的行为不同于它内部的元素. struts2表单标签包括: form.textfield.passw ...

  3. 比Redis更快:Berkeley DB面面观

    比Redis更快:Berkeley DB面面观 Redis很火,最近大家用的多.从两年前开始,Memcached转向Redis逐渐成为潮流:而Berkeley DB可能很多朋友还很陌生,首先,我们简单 ...

  4. EntityManager方法简介

    EntityManager 是用来对实体Bean 进行操作的辅助类.他可以用来产生/删除持久化的实体Bean,通过主键查找实体bean,也可以通过EJB3 QL 语言查找满足条件的实体Bean.实体B ...

  5. Win7路由器设置过程

    随着应用win7系统的人越来越多,对于这个系统的应用就更多了,其中大家最关注的就是这个系统和路由器上网的问题.下面,我们就来讲解一下win7系统的路由器的设置过程. 首先打开浏览器,在地址栏输入192 ...

  6. 笔记本_hp

    1.技术支持 http://support.hp.com/cn-zh 2.搜到的信息:“http://forum.51nb.com/thread-1080424-1-1.html” Product N ...

  7. AFNetworking请求中含有中文时程序崩溃

    出现error: Assertion failure in -[AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error ...

  8. hdu4720Naive and Silly Muggles

    链接 一直理解的最小覆盖圆就是外接圆..原来还要分钝角和锐角... 钝角的话就为最长边的中点,对于这题分别枚举一下外接圆以及中点的圆,判一下是不是在园外. #include <iostream& ...

  9. mysql 索引的原理

    1.考虑下面的情况,mysql> desc student;+----------+-------------+------+-----+---------+-------+| Field | ...

  10. 新建我的 第一个maven项目

    maven是管理项目的,myeclipse是编写代码的.第一次写项目都要配置好多东西,很麻烦,now 来看看怎样新建一个maven项目. 百度经验:jingyan.baidu.com 工具/原料   ...