【问题描述】在采用序列化,将多个对象追加到文件中;然后反序列化将文件中的所有对象取出时,出现java.io.StreamCorruptedException: invalid type code: AC多次修改都出现类似异常:比如java.io.StreamCorruptedException: invalid type code:00异常。
练习背景:梦阳辰在这里忙活了一下午,先是用集合存储对象,然后调用方法将集合写入,后面发现,集合只有一个,而多次调用方法将对象add到集合中,但是其多次调用,导致执行了多次writeObject方法,其将集合多次写入到文件中。这导致只能输出第一个添加进去的对象,因此在这里研究了一下午,哈哈。最后梦阳辰采用下列的方案二解决问题。梦阳辰忙活的是:Java实现学生信息管理系统(序列化和反序列化)
分析原因:new了多次ObjectOutputStream对象,然后用writeObject()写入对象后关闭流;但是用ObjectInputStream读取对象时,只new了一次ObjectInputStream,然后用其读取对象就会报异常,第一个对象是可以正常读出来,第二个对象就会报异常。原因是在新建一个ObjectOutputStream对象时会首先在前面写入两个Short类型的数据,就是四个字节,可以去看看ObjectOutputStream的构造方法,里面调用了一个writeStreamHeader()方法,在这个方法中写入了两个Short类型的头部数据。因此我们可以处理的方法就又很多,比如重写writeStreamHeader()方法,或者在写入文件得时候不写入头部,也可以在读取的时候,从第二次开始选择不读头部。
- 解决方案一(选择在读入的时候不读取头部):
public class Test1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("src/Day1/test.txt");
FileOutputStream fos = new FileOutputStream(file, true);
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(new User("a1", "18")); //写入第一个对象
oos.close();//关闭流
FileOutputStream fos1 = new FileOutputStream(file, true);
ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
oos1.writeObject(new User("b1", "22"));//写入第二个对象
oos1.close();
FileInputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis);
User user = (User) ois.readObject();
System.out.println(user);//读取第一个对象
byte[] T= new byte[4];
fis.read(T);
user = (User) ois.readObject(); //读取第二个对象
System.out.println(user);
}
}
class User implements Serializable{
private String name;
private String age;
public User() {
}
public User(String name, String age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
注意:这里为了便于观察,将两个类写在了同一源文件,将所有异常抛给了JVM,不建议采取。
- 解决方案二:只new一次ObjectOutputStrea和ObjectInputStream对象,多次调用writeObject和readObject方法进行序列化和反序列化。
这里可以参考一下梦阳辰写的:Java实现学生信息管理系统(序列化和反序列化)加深印象。 - 方案三:重写ObjectOutputhoStream 的 writeStreamHeader()方法。
- **方案四:**不写入头部。
扩展思考:1.只new一次ObjectOutputStream,ObjectInputStream对象实现可以吗?
- 第一种方式,将对象添加到集合或者数组中,然后就可以遍历数组或者集合中的对象达到效果。
public class ObjectInputStreamTest1 {
public static void main(String[] args) {
ObjectOutputStream oss =null;
List<Student> list= new ArrayList<>();
list.add(new Student(33,"adsfsa"));
list.add(new Student(36,"aa"));
list.add(new Student(39,"adsa"));
try {
// 序列化
oss = new ObjectOutputStream(new FileOutputStream("Students"));
//序列化对象
oss.writeObject(list);
oss.flush();
} catch (IOException e) {
e.printStackTrace();
}
finally {
if(oss!=null){
try {
oss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
class Student implements Serializable{//可序列化接口
private int no;
private String name;
public Student() {
}
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
注意反序列化的时候需要将readObject()方法进行强转,因为默认得到List类型。
2.ObjectOutputStream和ObjectInputStream实例化相同数量,然后分别调用writeObject和readObject()。
- 这种方式执行是完全Ok的, 但是想用不同的ObjectInputStream对象()的readObject方法依次取出所有的对象,这是不行的,每次只会取第一个对象。(退一万步将,就算可以,耗内存的同时又代码冗长。)
如果发现不足,或是有更好的方案,期望你在评论区留下你宝贵的建议。谢谢!