【问题标题】:Java writeObject() to always overwrite existing object, never append?Java writeObject() 总是覆盖现有对象,从不追加?
【发布时间】:2013-08-16 02:23:06
【问题描述】:

我想使用 ObjectOutputStream.writeObject() 将 Hashtable 序列化为文件,但我希望编写器始终覆盖任何现有对象,以便只存在一个对象:

FileOutputStream fout = new FileOutputStream( f);
ObjectOutputStream writer = new ObjectOutputStream(fout);
writer.writeObject( hashtable);
writer.flush();

hastable 在运行时会间歇性更新,因此我使用辅助方法来保持其状态:

private void persistObject( Hashtable ht){
   writer.writeObject( ht);
   writer.flush();
}

问题是每次我调用writeObject()时,文件都会附加一个新的hastable;有没有办法只覆盖文件中的任何内容,以便只保留一个对象?

【问题讨论】:

  • 我认为这是FileOutputStream 的默认行为,覆盖,而不是附加...可能是错误的:P
  • @MadProgrammer 如果您保持文件打开,这不是默认行为;它只是不断追加。
  • 是的,没有那样阅读代码

标签: java object file-io


【解决方案1】:

最简单的方法是关闭并重新打开文件,只需在您的 persistObject() 方法中重新运行此代码:

private void persistObject( Hashtable ht){
    try (ObjectOutputStream writer = new ObjectOutputStream(new FileOutputStream(f))) {
        writer.writeObject( hashtable);
        writer.flush();
        writer.close();
    }
}

【讨论】:

  • (...别忘了关闭流...)
  • 是的,我想过,但我必须保持文件打开。它是一个服务器,哈希表保持经常变化的临界状态;希望避免打开/关闭文件的开销。
  • Gotcha - IMO 的开销可能不值得担心。在现代操作系统上,它会非常小,而且内核通常可以很好地缓存与经常使用的文件相关的信息(即,如果你经常这样做,打开和关闭文件的开销并不多) .哈希表有多大(大约)?
  • 不大,几千个对象
  • 我同意@StevenSchlansker 的观点——close() 是必要的,flush() 不是。这个问题涵盖了它:stackoverflow.com/questions/2732260
【解决方案2】:

如果您想丢弃文件内容并重新开始写入文件的开头,您可以获取它的频道并重新定位内容。问题是,ObjectStreams 使用一些内部标头,如果我们想“重置”流,我们需要考虑这一点。

在实例化 ObjectOutputStream 之后,存储初始位置(writeStreamHeader 写入两个短裤 - 消耗 4 个字节,但最好是通用的而不是抱歉),以便您可以定位通道而不会覆盖标题。

final long initialPosition = fout.getChannel().position();

然后,每次你必须写东西时,跳过初始字节(通过定位通道),重置流并写入你的对象:

//java 1.7
private void persistObject(Hashtable ht){
   fout.getChannel().position(initialPosition);
   fout.reset();
   writer.writeObject(ht);
   writer.flush();
}
//java 1.6
private void persistObject(Hashtable ht){
   fout.getChannel().position(initialPosition);
   fout.getChannel().truncate(initialPosition);
   writer.writeObject(ht);
   writer.flush();
}

正如@bgp 所提到的,我不认为打开/关闭文件真的会引入那么多开销......而且我不认为在每次写入时截断并刷新到磁盘是有效的,或者保持文件流长时间打开是一种好习惯(或安全),但破解ObjectOutputStream 仍然很有趣。

【讨论】:

  • 很好 - 正在寻找那个,我没有测试过,但这看起来像 @raffian 所要求的。
  • 我使用您的方法写入文件,但在启动时我读取了文件;这不是问题的一部分,但如果您的方法有效,则内容应该可以使用readObject() 读取。我会玩弄它,
  • 这行不通。对象流标头将被删除或覆盖。
  • 该死的,你不会退出吧?我明天会玩这个,ty
  • 工作得很好,我更新了你的答案以反映 1.6 和 1.7 的差异,再次感谢。
【解决方案3】:

您应该在persistObject 中打开/写入/关闭ObjectOutputStream。您不应该在位置 0 处截断文件。ObjectOuputStream 具有复杂的结构,不仅是对象数据,还有标头、类描述符等。截断会破坏它,在尝试读取它时导致 StreamCorruptedException。

【讨论】:

    猜你喜欢
    • 2021-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 2016-06-19
    • 2012-07-13
    • 1970-01-01
    相关资源
    最近更新 更多