关闭字符串写入器会不会导致泄漏?
我意识到在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释放。