问题
我在做案例项目时出现了这个问题:在我读取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的实现比较简单,在这不过多赘述,创建一个集合,放入对象后,序列化集合就行!
方法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