不,不关闭 StringWriter 不会导致泄漏:如前所述,StringWriter#close() 是一个 nop,写入器只保存内存,而不是外部资源,所以这些将在写入器被收集时被收集。 (明确地说,它包含对不转义对象的私有字段中的对象的引用,具体来说是StringBuffer,因此没有外部引用。)
此外,您通常不应该关闭StringWriter,因为它会将样板代码添加到您的代码中,从而模糊了主要逻辑,正如我们将看到的。但是,为了让读者放心,您是故意这样做的,我建议您评论以下事实:
// Don't need to close StringWriter, since no external resource.
Writer writer = new StringWriter();
// Do something with writer.
如果你确实想关闭 writer,最优雅的方法是使用 try-with-resources,当你退出 try 块的主体时,它会自动调用 close():
try (Writer writer = new StringWriter()) {
// Do something with writer.
return writer.toString();
}
但是,由于 Writer#close() 抛出 IOException,您的方法现在也需要抛出 IOException 即使它从未发生,或者您需要捕获它,以向编译器证明它被处理。这是相当复杂的:
Writer writer = new StringWriter();
try {
// Do something with writer, which may or may not throw IOException.
return writer.toString();
} finally {
try {
writer.close();
} catch (IOException e) {
throw new AssertionError("StringWriter#close() should not throw IOException", e);
}
}
这个级别的样板文件是必要的,因为您不能只在整个 try 块上添加一个 catch,否则您可能会不小心吞下代码主体抛出的 IOException。即使目前没有,将来也可能会添加一些,并且您希望编译器对此进行警告。 AssertionError 正在记录 StringWriter#close() 的当前行为,这可能会在未来的版本中发生变化,尽管这种可能性极小;它还掩盖了 try 主体中可能发生的任何异常(同样,这在实践中绝不应该发生)。这是太多的样板和复杂性,您最好省略close() 并评论原因。
一个微妙的点是,不仅Writer#close() 会抛出一个IOException,StringWriter#close() 也是如此,因此您无法通过将变量设置为StringWriter 而不是Writer 来消除异常。这与 StringReader不同,后者重写 close() 方法并指定它不抛出异常!请参阅 my answer 至 Should I close a StringReader?。这看起来可能是错误的——为什么你会有一个什么都不做但可能抛出异常的方法? – 但大概是为了向前兼容,以便在将来关闭关闭IOException 的可能性保持开放,因为这对于作家来说是一个普遍的问题。 (也可能只是一个错误。)
总结一下:不关闭StringWriter 很好,但是不做通常正确的事情的原因,即try-with-resources,只是因为close() 声明它抛出了一个它没有的异常' t 实际上投入实践,并且精确地处理这个是很多样板。在任何其他情况下,最好只使用传统上正确的资源管理模式并防止出现问题和令人头疼的问题。