java.io.StreamCorruptedException: invalid type code: AC错误解决

问题

我在做案例项目时出现了这个问题:在我读取registerUser.txt里的所有对象时出现了java.io.StreamCorruptedException: invalid type code: AC

简单介绍一下我所在做的需求:

我想要完成的需求是从registerUser.txt文件中读取对象,获取对象的Uid属性。

代码如下:

public class Test {
    public static void main(String[] args) {
        try {
            isSame_unRegister();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static void isSame_unRegister() throws ClassNotFoundException, IOException {
        ArrayList<User> list = new ArrayList<>();
        //获取文件中的所有用户学号

        FileInputStream fis = new FileInputStream("src\\file\\registerUser.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);

        while (true) {
            try {
                list.add((User) ois.readObject());
                for (User user : list) {
                    System.out.println(user.getUId());
                }
            } catch (EOFException e) {
                //捕获异常时结束循环
                System.out.println("数据读出完成!");
                break;
            }
        }
        fis.close();
        ois.close();
    }
}

原因分析:

通过抛出的异常可知:

在 ObjectInputStream 类的 readObject0(boolean unshared) 方法中:

通过查询后发现,在ObjectInputStream和ObjectOutputStream的构造器中均有和ObjectInputStream对应的readStreamHeader()和与ObjectOutputStream对应的writeStreamHeader()方法

所以ObjectOutputStream在构造时,首先会将流的头部信息写入到文件中。然后在调用write时,写入其他数据,直到关闭。当再次使用ObjectOutputStream追加写入对象时,头部信息又会再次写入。所以当你用ObjectInputStream来读取对象时,流虽然能够将第一个头部信息跳过,但是其他头部信息会当做数据来处理,造成无法解析,所以读取会出现StreamCorruptedException: invalid type code: AC错误。

官方文档也说明了输出流的信息是由头部信息和数据组成的:

Primitive data, excluding serializable fields and externalizable data, is written to the ObjectOutputStream in block-data records. A block data record is composed of a header and data. The block data header consists of a marker and the number of bytes to follow the header.

解决方法:

知道了问题出现的原因后,可以有以下几个解决方法:

  1. 在写入文件的时候只写一个数据(和我的小伙伴哪里学到的方法)

可以将对象存入一个集合中,再将集合序列化,反序列化时也是对象集合
  1. 在写入文件的时候只传一个头部信息

  1. 不写入头部信息

方法1的实现比较简单,在这不过多赘述,创建一个集合,放入对象后,序列化集合就行!

方法2的实现:

public class AppendObjectOutputStream extends ObjectOutputStream {
    private static File file = null;
    public static File getFile() {
        return file;
    }
    public static void setFile(File file) {
        AppendObjectOutputStream.file = file;
    }

    /**
     * 调用父类的构造器,初始化父类
     */
    public AppendObjectOutputStream(File file) throws IOException {
        super(new FileOutputStream(file, true));
    }

    /**
     * 重写父类的 writeSteamHeader() 方法以实现文件中只存在一个StreamHeader
     *
     */
    @Override
    public void writeStreamHeader() throws IOException {
        // 如果文件为空直接写入 StreamHeader
        if (file == null || file.length() == 0) {
            super.writeStreamHeader();
        } else {
            // 文件中存在内容,则说明文件中已经存在了一个 StreamHeader,调用父类的 reset() 方法保证文件中只存在一个 StreamHeader
            this.reset();
        }
    }
}

方法3的实现:方法三如果不写入头部信息,那我们也不能读取头部信息,所以都需要重写readStreamHeader()和writeStreamHeader()方法。

public class MyObjectOutputStream extends ObjectOutputStream {

    public MyObjectOutputStream(OutputStream out) throws IOException {
        super(out);
    }

    @Override
    protected void writeStreamHeader() throws IOException {
        //重写读取头部信息方法:不写入头部信息
        super.reset();
    }
}
public class MyObjectInputStream extends ObjectInputStream {
    public MyObjectInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected void readStreamHeader() throws IOException {
        //重写读取头部信息方法:什么也不做
    }
}

最后我们通过这个类就可以获取我们需要的内容啦!

需要注意的是,如果你的文件中存有之前用ObjectOutputStream写入的数据,需要把它删除,再通过我们写的MyObjectOutputStream再写入数据,不然之前存入的头部信息依然存在而且还不能识别!

ok测试成功!

参考博客:

博主:假装四处看风景

文章链接:https://blog.csdn.net/Marco___/article/details/98380665

博主:Spring-_-Bear

文章链接:https://blog.csdn.net/weixin_51008866/article/details/121061149

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值