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. MusigCV安装

    首先,将下载的安装文件zip包,http://www.mathworks.com/products/compiler/mcr/ MCR2013a 然后依次执行下面的命令: 进入目录:cd /tmp 解 ...

  2. ABAP锁、数据库锁

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  3. HUD 2203 亲和串

    HUD 2203 亲和串 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768K (Java/Others) [题目描述 - ...

  4. canvas背景透明

    var can=document.getElementById("canv"); c=can.getContext("2d"); c.globalAlpha=. ...

  5. 战胜C语言中令人头疼的问题

    C语言一共32个关键字,下面一一列出:   1.auto声明自动变量 在默认情况下,编译器默认所有变量都是auto 2.int声明整型变量 3.double声明双精度变量 4.long声明长整型变量 ...

  6. 13 SELECT 以外的内容

    Insert 直接路径法 这种方法不去查找已有块中的空间, 它直接从高水位之上开始插入数据. 直接使用的是 nologging模式, 记住默认情况下通过直接路径插入进行加载的表上的索引仍然是会产生un ...

  7. Python学习笔记4—列表

    列表定义 >>> a=['] >>> type(a) <type 'list'> >>> bool(a) #列表有值,则为true T ...

  8. linux gksu用法

    apt-get install gksu gksu是linux下图形化的su/sudo工具 sudo 用来执行命令行(CLI)程序 gksu 用来执行图形的(GUI)程序 GUI = Graphica ...

  9. Linux 中如何卸载已安装的软件

    Linux 中如何卸载已安装的软件. Linux软件的安装和卸载一直是困扰许多新用户的难题.在Windows中,我们可以使用软件自带的安装卸载程序或在控制面板中的“添加/删除程序”来实现.与其相类似, ...

  10. linux 跨IP拷贝命令 scp

    原文:http://blog.csdn.net/mexican_jacky/article/details/52847094 scp -r ROOT/ tms2api@10.230.4.215:/Ja ...