在 try-with-resource 中手动关闭

Posted

技术标签:

【中文标题】在 try-with-resource 中手动关闭【英文标题】:Manual closing inside try-with-resource 【发布时间】:2014-01-21 09:50:21 【问题描述】:

假设我正在使用一个文档记录不佳的第三方库,它没有可用的源代码。该库的一种方法接受InputStream 来加载各种数据。

由于缺乏文档,尚不清楚该方法是否在完成后关闭流,因此一种可能的解决方案可能是将调用包装在 try-with-resource 中,只是为了在安全的一面。

不幸的是,Java 规范(据我所知)没有提及如果资源会发生什么。有人碰巧知道吗?

【问题讨论】:

你可以试试看,不是吗? @Fildor 通常只“尝试和猜测”而不是查看文档是一种不好的做法(即使在那里,文档也很差)。仅仅因为函数为 -11 返回 1 并不能使其成为 abs 函数。 @Xenos 我的意思不是“尝试和猜测”。我的意思是黑盒测试。如果文档很差,您想尽可能多地了解该功能。所以你会在它上面拍摄一个复杂的测试集(至少我会)。然后我会编写一些稳定的代码,即使可疑函数的行为很有趣。而且我从来没有说过“而不是查看文档”。顺便说一句:我遇到文档完全错误 【参考方案1】:

这将完全取决于资源本身的实现。 try-with-resource 语句是在 finally 块内调用 close()(并保留异常等)的“只是”句法糖(但太棒了)。

只要流支持close() 被调用两次——我希望大多数实现都这样做,并且InputStream 的合同要求——它绝对没问题。

请注意,您将处于与熟悉的将一个资源包装在另一个资源中的情况完全相同的情况下,例如

try (InputStream input = new FileInputStream(...)) 
   try (Reader reader = new InputStreamReader(input, ...)) 
       ...
   

或者使用单个 try-with-resources 语句:

try (InputStream input = new FileInputStream(...);
     Reader reader = new InputStreamReader(input, ...)) 
   ...

在这两种情况下都会有两个finally 块,因此首先调用reader.close(),然后调用input.close() - 但无论如何reader.close() 将关闭input

【讨论】:

@MarkoTopolnik:我也添加了单一版本。两者都有效,但有些人可能会发现第一个更容易推理。 @MarkoTopolnik:第二种形式中的逗号是经过深思熟虑的 - 这是一个尝试使用资源获取两个资源 但是它不能编译,我已经检查过了。你也检查过它,它对你有用吗?老实说,我会感到惊讶,因为这与 Java 语法的其余部分不一致。 @MarkoTopolnik:不,我没有检查过——抱歉。现在回滚了。分号对我来说确实看起来很不对。 这就是逻辑:逗号用于单个声明语句,涉及多个相同类型的变量。因此,如果您声明两个 InputStreams,它也可以在这里工作。【参考方案2】:

Closeable(因此,InputStream)的方法 close() 必须是幂等的:

如果流已经关闭,则调用此方法无效。

因此,多次关闭InputStream 是安全的。

但是,更通用的AutoCloseable 接口不需要其close() 方法是幂等的,因此对Closeable 以外的资源执行相同操作可能是不安全的:

注意,与Closeable的close方法不同,这个close方法不需要是幂等的。换句话说,多次调用此 close 方法可能会产生一些可见的副作用,这与 Closeable.close 不同,如果多次调用则要求无效。但是,强烈建议此接口的实现者使其 close 方法具有幂等性。

【讨论】:

【参考方案3】:

规范确实说明了一切:如果resource.close() 抛出异常,该异常将从构造中抛出。

当然,规范无法知道任何特定的close 方法是否会抛出异常。要找出这一点,您必须测试您的特定资源。

【讨论】:

【参考方案4】:

你可以尝试在finally语句中关闭它。

InputStream stream = null;
try 
  stream = new InputStream();
  yourMethod(stream);
 catch (...) 

 finally 
  try 
    stream.close()
   catch (IOException ioe) 
    // can throw IOException while closing closed stream
  

【讨论】:

以上是关于在 try-with-resource 中手动关闭的主要内容,如果未能解决你的问题,请参考以下文章

使用 try-with-resources 优雅关闭资源

Java进阶知识点3:更优雅地关闭资源 - try-with-resource语法

深入理解 Java try-with-resource 语法糖

第9项:尽量使用try-with-resources而不是try-finally(Prefer try-with-resources to try-finally)

一篇文章带你深入理解 try-with-resource

在 JDK 9 中更简洁使用 try-with-resources 语句