不,不关闭 aStringWriter
不会导致泄漏:如前所述,StringWriter#close()
它是一个 nop,并且 writer 只保存内存,而不是外部资源,因此在收集 writer 时会收集这些资源。(明确地说,它包含对私有字段中的对象的引用,这些对象不会转义对象,具体来说是 a StringBuffer
,因此没有外部引用。)
此外,您通常不应该关闭 a StringWriter
,因为它会将样板代码添加到您的代码中,从而模糊了主要逻辑,正如我们将看到的。但是,为了让读者放心,您正在小心并有意这样做,我建议您评论这个事实:
// Don't need to close StringWriter, since no external resource.
Writer writer = new StringWriter();
// Do something with writer.
如果您确实想关闭编写器,最优雅的是使用try-with-resourcesclose()
,它会在您退出 try 块的主体时自动调用:
try (Writer writer = new StringWriter()) {
// Do something with writer.
return writer.toString();
}
但是,由于Writer#close() throws IOException
,您的方法现在也需要 throwIOException
即使它从未发生,或者您需要捕获它,以向编译器证明它已被处理。这涉及到很多:
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()
抛出 an IOException
,而且也会抛出StringWriter#close()
,因此您无法通过使用变量 aStringWriter
而不是a 来消除异常Writer
。这与 String Reader不同,后者覆盖方法并指定它不抛出异常!请参阅我对是否应该关闭 StringReader的回答?. 这看起来可能是错误的——为什么你会有一个什么都不做但可能抛出异常的方法?– 但可能是为了向前兼容,以便为将来关闭关闭的可能性留有余地,因为这对于作家来说是一个普遍的问题。(也可能只是一个错误。)close()
IOException
总结一下:不关闭 a 很好StringWriter
,但不做通常正确的事情的原因,即 try-with-resources,只是因为close()
它声明它抛出了一个它实际上并没有抛出的异常,并处理这个正是很多样板。在任何其他情况下,最好只使用传统上正确的资源管理模式并防止出现问题和令人头疼的问题。