Java序列化存储多对象遇StreamCorruptedException,求原因及优化方案
问题:序列化多个邮件对象到单个文件时抛出StreamCorruptedException,求解决方案
我正在为课程制作一个邮件客户端,希望将每日发送的邮件消息存储到单个文件中,计划使用Java序列化实现。尝试通过在FileOutputStream中设置append=true进行追加写入,但抛出了StreamCorruptedException,堆栈跟踪如下。无法找到报错原因,同时想了解使用序列化存储多个对象到单个文件的更佳方案,恳请提供帮助。
读写对象的代码
//For reading and writing objects to files interface MyFileHandler<T> { public void write(T input ); public ArrayList<T> read(); } class FileHandlerObject implements MyFileHandler<EmailMessage> { public void write(EmailMessage input){ try{ FileOutputStream fileOutputStream = new FileOutputStream("emails.ser",true); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); objectOutputStream.writeObject(input); objectOutputStream.flush(); objectOutputStream.close(); } catch(IOException e) { e.printStackTrace(); } } public ArrayList<EmailMessage> read(){ ArrayList<EmailMessage> messages=new ArrayList<EmailMessage>(); try{ FileInputStream fileInputStream=new FileInputStream("emails.ser"); ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream); boolean cont = true; while (cont) { if(fileInputStream.available()!=0) { Object obj=objectInputStream.readObject(); EmailMessage message=(EmailMessage)obj; messages.add(message); } else{ cont=false; } } objectInputStream.close(); return messages; } catch(IOException e) { e.printStackTrace(); } catch(ClassNotFoundException c) { c.printStackTrace(); } catch(ClassCastException c) { c.printStackTrace(); } return null; } public static void main(String[] args) { FileHandlerObject f=new FileHandlerObject(); ArrayList<EmailMessage> a=f.read(); a=f.read(); } }
序列化的EmailMessage类
public class EmailMessage implements Serializable{//implement serializables private String recipient; private String subject; private String content; private String date; public void setRecipient(String recipient) { this.recipient=recipient; } public String getRecipient() { return this.recipient; } public void setSubject(String subject){ this.subject=subject; } public void setContent(String content){ this.content=content; } public String getSubject(){ return this.subject; } public String getContent(){ return this.content; } public void setDate(String date) { this.date=date; } public String getDate() { return this.date; } public String printDetails() { String details="Recipient: "+getRecipient()+ "\nSubject: "+getSubject()+ "\nEmail content: "+getContent()+ "\nDate Sent: "+getDate(); return details; }}
报错堆栈跟踪
java.io.StreamCorruptedException: invalid type code: AC at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1723) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:508) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:466) at FileHandlerObject.read(MyFileHandler.java:46) at FileHandlerObject.main(MyFileHandler.java:89)
原因与解决方案
报错原因
- 重复写入序列化头部:每次调用
write方法时都会创建新的ObjectOutputStream,该流初始化时会向文件写入序列化头部信息。追加模式下文件会存在多个头部,ObjectInputStream只能识别第一个,后续读取到额外头部时就会抛出StreamCorruptedException(错误码AC对应额外头部的标识)。 - 错误的读取终止判断:
read方法中用fileInputStream.available()!=0判断是否还有对象可读是错误的,available()仅返回当前可读取的字节数,无法准确判断序列化流是否结束。
方案一:修正追加写入逻辑(保留追加模式)
通过判断文件是否为新文件,避免重复写入序列化头部;同时修正读取逻辑,用EOFException判断流结束:
修正后的write方法
public void write(EmailMessage input){ boolean isNewFile = !new File("emails.ser").exists(); try{ FileOutputStream fileOutputStream = new FileOutputStream("emails.ser", true); ObjectOutputStream objectOutputStream; if (isNewFile) { // 新文件正常写入头部 objectOutputStream = new ObjectOutputStream(fileOutputStream); } else { // 已有文件跳过头部写入 objectOutputStream = new ObjectOutputStream(fileOutputStream) { @Override protected void writeStreamHeader() throws IOException { reset(); } }; } objectOutputStream.writeObject(input); objectOutputStream.flush(); objectOutputStream.close(); } catch(IOException e) { e.printStackTrace(); } }
修正后的read方法
public ArrayList<EmailMessage> read(){ ArrayList<EmailMessage> messages=new ArrayList<>(); File file = new File("emails.ser"); if(!file.exists()){ return messages; } try( FileInputStream fileInputStream=new FileInputStream(file); ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream) ){ while (true) { try { Object obj=objectInputStream.readObject(); if(obj instanceof EmailMessage){ messages.add((EmailMessage)obj); } } catch(EOFException e) { // 捕获EOFException表示流读取完毕 break; } } return messages; } catch(IOException | ClassNotFoundException e) { e.printStackTrace(); } catch(ClassCastException c) { c.printStackTrace(); } return null; }
方案二:序列化整个集合(更简洁)
如果邮件数量不大,每次写入时将所有邮件存入ArrayList并整体序列化,读取时直接读取整个集合,这种方式逻辑更简单,也不会出现头部重复的问题:
改写后的write方法
public void write(EmailMessage input){ // 先读取已有邮件 ArrayList<EmailMessage> existingMessages = read(); if(existingMessages == null){ existingMessages = new ArrayList<>(); } existingMessages.add(input); // 整体写入集合对象 try( FileOutputStream fileOutputStream = new FileOutputStream("emails.ser"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream) ){ objectOutputStream.writeObject(existingMessages); } catch(IOException e) { e.printStackTrace(); } }
改写后的read方法
public ArrayList<EmailMessage> read(){ File file = new File("emails.ser"); if(!file.exists()){ return new ArrayList<>(); } try( FileInputStream fileInputStream=new FileInputStream(file); ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream) ){ Object obj = objectInputStream.readObject(); if(obj instanceof ArrayList){ return (ArrayList<EmailMessage>)obj; } } catch(IOException | ClassNotFoundException e) { e.printStackTrace(); } return null; }
方案三:自定义文件格式(适合大文件)
如果邮件数量极大,不适合每次读取整个集合,可以在文件开头记录对象总数,每次追加时更新总数;或者在每个对象之间写入自定义分隔符,读取时先识别分隔符再读取对象。这种方式实现复杂,但适合处理大体积文件。
内容的提问来源于stack exchange,提问作者scout
相关产品推荐
相关产品推荐

