关闭字符串写入器会不会导致泄漏?

我意识到在java中GC最终会清理对象,但我问的是不关闭你的字符串编写器是不好的做法,目前我这样做:

private static String processTemplate(final Template template, final Map root) { StringWriter writer = new StringWriter(); try { template.process(root, writer); } catch (TemplateException e) { logger.error(e.getMessage()); } catch (IOException e) { logger.error(e.getMessage()); } finally { } return writer.toString(); } 

我应该关闭编写器并创建一个像这样的新String:

 String result = ""; ... finally { result = writer.toString(); writer.close(); } 

这样做更好吗?

javadoc非常明确:

关闭StringWriter无效。

快速查看代码就可以确认:

 public void close() throws IOException { } 

它没有持有任何非内存资源。 它将像其他任何东西一样被垃圾收集。 close()probabaly仅存在,因为其他writer对象确实拥有需要清理的资源,并且需要close()来满足界面。

不,不关闭StringWriter不会导致泄漏:如上所述, StringWriter#close()是一个nop,并且编写器只保存内存,而不是外部资源,因此在收集编写器时将收集这些内容。 (显然,它保存对私有字段中不会转义对象的对象的引用,具体来说是StringBuffer ,因此没有外部引用。)

此外,您通常不应该关闭StringWriter ,因为它会为您的代码添加样板,模糊主逻辑,正如我们将看到的那样。 但是,为了让读者放心,你小心谨慎并且故意这样做,我建议评论这个事实:

 // Don't need to close StringWriter, since no external resource. Writer writer = new StringWriter(); // Do something with writer. 

如果你想关闭编写器,最优雅的是使用try-with-resources ,当你退出try块的主体时会自动调用close()

 try (Writer writer = new StringWriter()) { // Do something with writer. return writer.toString(); } 

但是,由于Writer #close()抛出IOException ,您的方法现在也需要抛出IOException 即使它永远不会发生 ,或者您需要捕获它,以向编译器certificate它已被处理。 这非常复杂:

 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的主体中可能发生的任何exception(同样,这在实践中永远不会发生)。 这是太多的样板和复杂性,你最好省略close()和评论原因。

一个微妙的问题是, Writer#close()抛出IOException ,而且StringWriter#close()也是如此,所以你不能通过使变量成为StringWriter而不是Writer来消除exception。 这与String Reader 不同 ,后者覆盖close()方法并指定它不会抛出exception! 请参阅我的答案我 应该关闭StringReader吗? 。 这可能看起来不对 – 为什么你会有一个方法什么都不做,但可能会抛出exception? – 但可能是为了向前兼容,以便在将来关闭时抛出IOException的可能性,因为这对作者来说通常是一个问题。 (这也可能只是一个错误。)

总结一下:没有关闭StringWriter是好的,但不做通常正确的事情的原因,即try-with-resources,只是因为close()声明它抛出了一个它实际上没有在实践中抛出的exception并且正确处理这个问题是很多样板。 在任何其他情况下,最好只使用传统上正确的资源管理模式,并防止出现问题和头疼。

在方法结束时,编写器没有任何引用,因此它将被GC释放。