Java序列化及反序列化将多个对象追加到文件并读取多个对象(ObjectOutputStream,ObjectInputStream)

【问题描述】在采用序列化,将多个对象追加到文件中;然后反序列化将文件中的所有对象取出时,出现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方法依次取出所有的对象,这是不行的,每次只会取第一个对象。(退一万步将,就算可以,耗内存的同时又代码冗长。)

如果发现不足,或是有更好的方案,期望你在评论区留下你宝贵的建议。谢谢!

在这里插入图片描述

发布了56 篇原创文章 · 获赞 33 · 访问量 5636

猜你喜欢

转载自blog.csdn.net/jiahuan_/article/details/105559966
今日推荐