Java 之 I/O 系列 02 ——序列化(二)
Java 之 I/O 系列 目录
继续上篇的第二个问题
如果一个类实现了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 ——序列化(二)的更多相关文章
- Java 之 I/O 系列 02 ——序列化(一)
Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 一 序列化概述 序 ...
- Java 之 I/O 系列 01 ——基础
Java 之 I/O 系列 目录 Java 之 I/O 系列 01 ——基础 Java 之 I/O 系列 02 ——序列化(一) Java 之 I/O 系列 02 ——序列化(二) 整理<疯狂j ...
- Java 集合系列 02 Collection架构
java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...
- java io系列02之 ByteArrayInputStream的简介,源码分析和示例(包括InputStream)
我们以ByteArrayInputStream,拉开对字节类型的“输入流”的学习序幕.本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的 ...
- Java NIO系列教程(二) Channel通道介绍及FileChannel详解
目录: <Java NIO系列教程(二) Channel> <Java NIO系列教程(三) Channel之Socket通道> Channel是一个通道,可以通过它读取和写入 ...
- Java 设计模式系列(十二)策略模式(Strategy)
Java 设计模式系列(十二)策略模式(Strategy) 策略模式属于对象的行为模式.其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换.策略模式使得算法可以 ...
- Java练习 SDUT-2733_小鑫の日常系列故事(二)——石头剪子布
小鑫の日常系列故事(二)--石头剪子布 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 小鑫在上幼儿园的时候,喜欢跟小伙 ...
- 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...
- Java内存泄漏分析系列之二:jstack生成的Thread Dump日志结构解析
原文地址:http://www.javatang.com 一个典型的thread dump文件主要由一下几个部分组成: 上图将JVM上的线程堆栈信息和线程信息做了详细的拆解. 第一部分:Full th ...
随机推荐
- oracle默认管理员的帐号和密码以及密码修改和解除锁定
安装 ORACLE 时,若没有为下列用户重设密码,则其默认密码如下: 用户名 / 密码 登录身份 说明 sys/change_on_install SYSDBA 或 SYSOPER 不能以 NORMA ...
- [YY]已知逆序列求原序列(二分,树状数组)
在看组合数学,看到逆序列这个概念.于是YY了一道题:已知逆序列,求出原序列. 例子: 元素个数 n = 8 逆序列 a={5,3,4,0,2,1,1,0} 则有原序列 p={4,8,6,2,5,1,3 ...
- python_way day17 jQuery表单验证,事件绑定,插件,文本框架,正则表达式
python_way day17 1.jQuery表单验证 dom事件绑定 jquery事件绑定 $.each return值的判断 jquery扩展方法 2.前段插件 3.jDango文本框架 4. ...
- yii-mail yii 发送邮件
参考网址:http://shoukii0721.iteye.com/blog/1576225 有很多时候我们需要给用户发送邮件,作留言,或者是激活邮件.等用途. 需要注意的是,设置发送的邮件得有SMT ...
- Windows Live Writer编写Octopress
Windows live Writer是一个可以用来离线编写,并发布博客的工具. Octopress是一个静态博客生成系统.使用群体多是geek,主要有显示代码清晰,git同步,并且不用购买空间的特点 ...
- 一个不错的能将HTML表格导出为excel,pdf等的jquery插件
https://github.com/kayalshri/tableExport.jquery.plugin https://github.com/kayalshri/ tableExport.jqu ...
- mysql批量替换数据库某字段部分内容
update 表名 set 字段名=replace(字段名,’要替换的内容’,’替换后的内容’) eg:修改scenario表中的picture字段中的ip地址. UPDATE scenario SE ...
- JavaScript的事件对象_鼠标事件
鼠标事件是 Web 上面最常用的一类事件,毕竟鼠标还是最主要的定位设备.那么通过事件对象可以获取到鼠标按钮信息和屏幕坐标获取等. 一.鼠标按钮 只有在主鼠标按钮被单击时(常规一般是鼠标左键)才会触发 ...
- EL 表达式
转载自 :http://blog.csdn.net/qwerasdf123/article/details/4189889 EL表达式 1.EL简介 1)语法结构 ${expr ...
- chrome web开发工具
顾名思义Chrome开发工具就是一个工具,它允许Web开发人员可以通过浏览器应用程序干预和操作Web页面,也可以通过这个工具调试和测试Web页面或Web应用程序.有了这个工具,你可以做很多有趣的事情: ...