ArrayList序列化:如何向已存在的序列化文件追加新ArrayList?
关于Java序列化追加写入ArrayList的问题解答
嘿,这个序列化追加的坑我之前也踩过!先直接给你结论:直接用ObjectOutputStream加FileOutputStream(true)追加写入是行不通的,原因和解决方法我给你拆解清楚:
为什么直接追加会失败?
ObjectOutputStream在初始化的时候,会自动写入一个序列化流头部(Stream Header)。第一次写入文件时,这个头部是正常的,能被ObjectInputStream识别;但当你第二次追加写入时,新的ObjectOutputStream又会写入一个新的头部,而ObjectInputStream读取时,只会解析第一个头部,后续的头部会被当成无效数据,导致读取失败或者抛出异常。
两种可行的解决方案
方案一:合并现有列表后重写文件(你提到的可行方案)
这是最稳妥、最不容易出错的方式:每次添加新客户时,先把文件里已有的ArrayList<Customer>反序列化读出来,把新客户加入到这个列表里,再重新序列化覆盖整个文件。
示例代码:
// 先实现反序列化方法 public ArrayList<Customer> deserializeBank() { File file = new File("data.ser"); if (!file.exists()) { return new ArrayList<>(); } try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(file))) { return (ArrayList<Customer>) is.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); return new ArrayList<>(); } } // 然后修改序列化方法 public void serializeBank(ArrayList<Customer> newCustomers) { ArrayList<Customer> existingCustomers = deserializeBank(); existingCustomers.addAll(newCustomers); try (ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("data.ser"))) { os.writeObject(existingCustomers); } catch (IOException i) { i.printStackTrace(); } }
方案二:自定义OutputStream跳过重复头部,实现真正的追加
如果你坚持要直接追加而不重写整个文件,可以自定义一个ObjectOutputStream的子类,覆盖writeStreamHeader()方法,当文件已经存在(即追加模式)时,跳过头部的写入。
示例代码:
// 自定义可追加的ObjectOutputStream class AppendableObjectOutputStream extends ObjectOutputStream { public AppendableObjectOutputStream(OutputStream out) throws IOException { super(out); } @Override protected void writeStreamHeader() throws IOException { // 追加模式下,不写入新的流头部 reset(); } } // 修改后的序列化方法 public void serializeBank(ArrayList<Customer> newCustomers) { File file = new File("data.ser"); boolean isAppend = file.exists() && file.length() > 0; try (OutputStream fos = new FileOutputStream(file, true); ObjectOutputStream os = isAppend ? new AppendableObjectOutputStream(fos) : new ObjectOutputStream(fos)) { os.writeObject(newCustomers); } catch (IOException i) { i.printStackTrace(); } }
注意:读取的时候要循环读取,直到文件结束:
public ArrayList<Customer> deserializeBank() { ArrayList<Customer> allCustomers = new ArrayList<>(); File file = new File("data.ser"); if (!file.exists()) { return allCustomers; } try (ObjectInputStream is = new ObjectInputStream(new FileInputStream(file))) { while (true) { try { ArrayList<Customer> customers = (ArrayList<Customer>) is.readObject(); allCustomers.addAll(customers); } catch (EOFException e) { // 读取到文件末尾,结束循环 break; } } } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } return allCustomers; }
额外提醒
- 确保
Customer类的serialVersionUID保持不变,否则反序列化时会抛出InvalidClassException。 - 追加写入的方式虽然节省了重写文件的时间,但如果文件损坏,可能会导致部分数据无法恢复;而重写整个文件的方式数据完整性更高。
内容的提问来源于stack exchange,提问作者robinhoodjr
相关产品推荐
相关产品推荐

