当前位置: 移动技术网 > IT编程>开发语言>Java > 关于java对象的反序列化时产生的异常补充:java.io.StreamCorruptedException: invalid type code: AC

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

2020年07月18日  | 移动技术网IT编程  | 我要评论

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网