本文主要是在使用IO流做图书馆管理系统时通过将图书、读者等对象作为类对象,将其所定义的对象的属性 进行整体存取。本文主要对其中的ObjectInputStream、ObjectOutputStream学习与理解并通过一定的例子进行总结。
ObjectInputStream | ObjectOutputStream | |
---|---|---|
概念 | 反序列化流 | 序列化流 |
作用 | 将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象,以流的方式读取对象。 | 把对象转成字节数据的输出到文件中保存,对象的输出过程称为序列化,可实现对象的持久存储。 |
一个对象要想序列化,必须满足两个条件
① 该类必须实现 java.io.Serializable 接口。
( Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出 NotSerializableException 的异常。)
import java.io.Serializable;
public static class Reader implements Serializable {
public String name = null;
public int age = 0;
}
如上,上述的静态类没有实现任何方法,可看出来它是一个标记接口。
② 该类的所有属性必须是可序列化的。
(如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。)
【注】对象序列化本身就是一个主题。Java IO系列教程主要关注流、reader和writer,目前在网上已经有很多文章探讨了对象序列化,链接如下:http://java.sun.com/developer/technicalArticles/Programming/serialization/
-----------------举个栗子
/*自定义的对象输出流
**/
public class MyObjectOutputStream extends ObjectOutputStream {
/**
* 重写writeStreamHeader()方法
*/
public MyObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
public void writeStreamHeader() throws IOException {
return;
}
//序列化写入
public static <T> void setFileObject(File file, T p) throws Exception {
FileOutputStream fos = new FileOutputStream(file, true);//可追加写入
ObjectOutputStream oos = new ObjectOutputStream(fos);//创建对象操作流
oos.writeObject(p);//写入
oos.close();//关闭流
}
}
【异常1抛出】对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个ClassNotFoundException 异常。
【异常2抛出】当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个 InvalidClassException 异常。
原因如下:
① 该类的序列版本号与从流中读取的类描述符的版本号不匹配
② 该类包含未知数据类型
③ 该类没有可访问的无参数构造方法
【注】除了实现Serializable接口外,用于序列化的类还应包含:
private static final long serialVersionUID=1200L;//值可自定
serialVersionUID的Java的对象序列化API使用该变量来确定反序列化的对象是否已使用相同版本的类进行序列化(写入),因为它现在正尝试对其进行反序列化。
想象一个Reader对象被序列化到磁盘上。然后对Reader集合进行更改。
之后,尝试反序列化存储的Reader对象。
现在,序列化的Reader对象可能与Reader该类的新版本不对应。
为了检测此类问题,实现的类Serializable应包含一个serialVersionUID字段。
如果对类进行了大的更改,则还应该更改其serialVersionUID的值。
解决方法:
① 修改本地的serialVersionUID为流中的serialVersionUID。
② 在当初实现Serializable接口时,就固定一个serialVersionUID,这样每次编译就不会自动生成一个新的serialVersionUID。
(白话说:只要有类implements Serializable,最好设置一个独特的 serialVersionUID )
//反序列化读出
public static <T> List<T> getFileObject(File file) throws Exception {
List<T> list = new ArrayList<T>();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = null;
// 判断是否存在
if (file.exists()) {
while (fis.available() > 0) {
// 每次都new一个新的ObjectInputStream:
ois = new ObjectInputStream(fis);//解决StreamCorruptedException异常
T p = (T) ois.readObject();
list.add(p);
}
ois.close();
return list;
} else {
System.out.println("不存在任何信息吖~");
return list;
}
}
【注】java.io.StreamCorruptedException: invalid type code: AC 问题解决
1、运用集合:
在第一次序列化对象之前,把要序列化的对象添加到集合中,把这个集合序列化到文件中。然后每次序列化之前,除了把准备序列化的对象添加到集合中,再把已经序列化的集合反序列化回来添加到集合中,然后再把集合序列化到文件中。
2、重写ObjectOutputSream的writeStreamHeader()方法:
判断是不是第一次写入,若是则写入头部,若不是则不写入头部。
3:不重写ObjectOutputSream的writeStreamHeader()方法。
在反序列化的while循环中,每次都创建一个新的ObjectInputStream用来读取header。(本文即采取此措施)
上述栗子中方法的应用:
// 增加读者基本信息
public void insertReaderInfo() throws Exception {
System.out.println("#####读者相关信息。读者编号,姓名,出生日期,性别,联系电话,专业,读者类型(1、一类 2、二类 3、三类)。");
Reader rm = scannerReaderInfo(); //键盘输入要插入的数据的方法,返回值是Reader对象。
MyObjectOutputStream.setReader(readerfile, rm); //写入数据到文件
}
// 查询读者信息
public void selectReaderInfo() throws Exception {
readerfile = new File("src\\dell\\librarymanagementsystem\\data\\readerInfo.txt");
readerslist = MyObjectOutputStream.getReader(readerfile);
// 查询所有用户
System.out.println("^_^所有读者用户的信息为:");
for (Reader readers : readerslist) {
//循环输出
System.out.println(readers);
}
System.out.println();
}
以上就是今天要讲的内容,本文仅仅简单介绍了使用IO对象操作流时会遇到的问题以及简单的使用吖,欢迎交流~