关于java对象的反序列化时产生的异常补充:java.io.StreamCorruptedException: invalid type code: AC
关于java对象的反序列化时产生的异常补充:java.io.StreamCorruptedException: invalid type code: AC
大家好,我是一位在java学习圈中不愿意透露姓名并苟且偷生的小学员,如果文章有错误之处,还望海涵,欢迎多多指正
问题描述:
// 在向一个文件写入可序列化对象时,每次在文件的末尾添加一个或多个可序列化的对象,
// 于是使用了FileOutputStream(文件名,true)间接的构建了ObjectOutputStream流对象
原因:
// 在一个文件都有一个文件的头部和文件体。由于对多次使用FileOutputStream(文件名,true)
// 构建的ObjectOutputStream对象向同一个文件写数据,
// 在每次写数据的时候他都会向这个文件末尾先写入header在写入你要写的对象数据,
// 在读取的时候遇到这个在文件体中的header就会报错。导致读出时,出现StreamCorrupted异常
特别注意:
//异常产生代码(程序执行时系统会自动添加header,即执行一次文件尾添加一个header)注意上面说的 “一次” 指的是执行一次运行过程,即java.exe
//由于用FileInputStream(文件名,true)向同一个文件中序列化对象,
//每“次”都会向文件中序列化一个header。在反序列化的时候每个
//ObjectInputStream 对象只会读取一个header,那么当遇到第二个的时候就会报错,导致出现异常
具体对象类的描述代码如下:
package io;
import java.io.Serializable;
public class ObjectPerson implements Serializable {
private static final long serialVersionUID = 2017019123524660527L;
private String name;
private String age;
public ObjectPerson(){}
public ObjectPerson(String name,String age){
this.name = name;
this.age = age;
}
public String getName(){
return this.name;
}
public String getAge(){
return this.age;
}
public String toString(){
return "{"+this.name+","+this.age+"}";
}
}
解决方法一 通过将对象添加入集合中,从文件中读出集合后再解析集合内容即可(此处样例采用HashSet,小伙伴们想用其他集合也可以根据你的对象内容来判断即可)
ObjectOutputStream oos = null;
try {
HashSet<ObjectPerson> hashSet = new HashSet<ObjectPerson>();
hashSet.add(new ObjectPerson("我","18"));
hashSet.add(new ObjectPerson("学习","20"));
File file = new File("E://test//Person.txt");
FileOutputStream fos = new FileOutputStream(file);
oos = new ObjectOutputStream(fos);
oos.writeObject(hashSet);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//不能直接将新添加的对象直接添加入文件内,
//每次添加都需先从文件读取出来,增加完后再全部重新输入到文件,感觉效率太低!
ObjectInputStream ois = null;
try {
FileInputStream fis = new FileInputStream(new File("E://test//Person.txt"));
ois = new ObjectInputStream(fis);
HashSet<ObjectPerson> set= (HashSet<ObjectPerson>)ois.readObject();
Iterator<ObjectPerson> i = set.iterator();
while(i.hasNext()){
ObjectPerson op = i.next();
System.out.println(op);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
解决方法二 重写ObjectOutputStream的writeStreamHeader()方法:
以下是自定义流类重写方法的具体实现代码:
//自己创建一个流的类 继承ObjectOutputStream实现方法重写,当不需要header时即文件长度(即file.length()) < 1 时创建自己定义的这个流类即可
package io;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class MyObjectOutputStream extends ObjectOutputStream {
public MyObjectOutputStream(OutputStream out) throws IOException {
super(out);
}
public MyObjectOutputStream() throws IOException, SecurityException {
super();
}
@Override
protected void writeStreamHeader() throws IOException{
return;
}
}
以下是方法二具体实现代码:
// 判断是不是第一次写入,若是则写入头部,若不是则不写入头部
ObjectOutputStream out = null;
ObjectInputStream in = null;
List<ObjectPerson> list = new ArrayList<ObjectPerson>();
list.add(new ObjectPerson("我", "18"));
list.add(new ObjectPerson("学习", "20"));
String path = "E://test//Person.txt";
try { //判断文件大小并调用不同的方法
File file = new File(path);
FileOutputStream fos = new FileOutputStream(file,true);
if (file.length() < 1) {
out = new ObjectOutputStream(fos);
} else {
out = new MyObjectOutputStream(fos);
}
//添加对象
for (int i = 0; i < list.size(); i++) {
out.writeObject(list.get(i));
}
out.flush();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
if(out != null) { //若流通道创建成功,保证流通道必定会被关闭
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(path)));
while (true) {
//EOFException说明读不到对象了
ObjectPerson op = (ObjectPerson) in.readObject();
System.out.println(op);
}
} catch (EOFException e) {
//可以跟我一样做个提示而不用抛出异常
System.out.println("读取完毕");
} catch (Exception ex) {
ex.printStackTrace();
}try {
if(in != null) { //若流通道创建成功,保证流通道必定会被关闭
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
解决方法三 在反序列化的过程中,每次都创建一个新的ObjectInputStream用来读取header,本次样例对象数据较少故未用集合做每次添加个数的记录,有兴趣的小伙伴可以自己尝试一下哦
// 我们先看看用while循环可能会产生的漏洞
ObjectOutputStream oos = null;
try {
FileOutputStream fos = new FileOutputStream(new File("E://test//Person.txt"),true);
oos = new ObjectOutputStream(fos);
oos.writeObject(new ObjectPerson("我", "18"));
oos.writeObject(new ObjectPerson("学习", "100"));
oos.writeObject(new ObjectPerson("世界", "1000000"));
oos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
ObjectInputStream ois = null;
try {
FileInputStream fis = new FileInputStream(new File("E://test//Person.txt"));
while(fis.available() > 0) {
ois = new ObjectInputStream(fis);
ObjectPerson op = (ObjectPerson)ois.readObject();
System.out.println(op);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
上面代码运行后结果为:
// {我,18}
// java.io.StreamCorruptedException: invalid stream header: 7371007E
那么为什么会出现这个异常呢,其实每一个ois对象都会只读一个header,如果没读到或者读多了就凉了,不妨细细看看文章刚开始对此header的描述吧,一脸奸笑嘿嘿(伏笔成功)
接下来我们看正确解决方法:
ObjectOutputStream oos = null;
try {
FileOutputStream fos = new FileOutputStream(new File("E://test//Person.txt"),true);
oos = new ObjectOutputStream(fos);
//第一次执行添加三个对象
oos.writeObject(new ObjectPerson("我", "18"));
oos.writeObject(new ObjectPerson("学习", "100"));
oos.writeObject(new ObjectPerson("世界", "1000000"));
//第二次执行添加两个对象
// oos.writeObject(new ObjectPerson("你", "20"));
// oos.writeObject(new ObjectPerson("他", "21"));
oos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(oos != null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
ObjectInputStream ois = null;
try {
FileInputStream fis = new FileInputStream(new File("E://test//Person.txt"));
//第一个ois读第一次添加的三个对象
ois = new ObjectInputStream(fis);
ObjectPerson op1 = (ObjectPerson)ois.readObject();
System.out.println(op1);
ObjectPerson op2 = (ObjectPerson)ois.readObject();
System.out.println(op2);
ObjectPerson op3 = (ObjectPerson)ois.readObject();
System.out.println(op3);
//第二个ois读第二次添加的两个对象
// ois = new ObjectInputStream(fis);
// ObjectPerson op4 = (ObjectPerson)ois.readObject();
// System.out.println(op4);
// ObjectPerson op5 = (ObjectPerson)ois.readObject();
// System.out.println(op5);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(ois != null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
看到此处是不是感觉豁然开朗呢,但是此方法实际操作大量对象数据时很耗空间,比如每次添加2个,那么每2个就要记录一次(根据每次添加的对象个数不同而不同),可以记录在集合中(因为集合长度可变),拿ArrayList举例,此时每一个索引对应的数据都是2,所以每读2个就要创建一个新的ois
看完了点个赞呗 据说会点赞关注的人运气都不差 嘿嘿皮一下很开心
本文地址:https://blog.csdn.net/bw_cx_fd_sz/article/details/107406096