欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于java对象的反序列化时产生的异常补充:java.io.StreamCorruptedException: invalid type code: AC

程序员文章站 2022-06-28 18:40:51
关于java对象的反序列化时产生的异常补充:java.io.StreamCorruptedException: invalid type code: AC大家好,我是一位在java学习圈中不愿意透露姓名并苟且偷生的小学员,如果文章有错误之处,还望海涵,欢迎多多指正问题描述:// 在向一个文件写入可序列化对象时,每次在文件的末尾添加一个或多个可序列化的对象,// 于是使用了FileOutputStream(文件名,true)间接的构建了ObjectOutputStrea...

关于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