You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

原因与解决方案

报错原因

  1. 重复写入序列化头部:每次调用write方法时都会创建新的ObjectOutputStream,该流初始化时会向文件写入序列化头部信息。追加模式下文件会存在多个头部,ObjectInputStream只能识别第一个,后续读取到额外头部时就会抛出StreamCorruptedException(错误码AC对应额外头部的标识)。
  2. 错误的读取终止判断: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 09:24:16