关闭Java FileInputStream
好的,我一直在进行以下操作(变量名已更改):
FileInputStream fis = null; try { fis = new FileInputStream(file); ... process ... } catch (IOException e) { ... handle error ... } finally { if (fis != null) fis.close(); }
最近,我开始使用FindBugs,这表明我没有正确关闭流。我决定看看是否可以使用finally {}块完成任何操作,然后,我明白了,哦,是的,close()会引发IOException。人们在这里应该做什么? Java库抛出太多已检查的异常。
解决方案
无论是在尝试关闭流时抛出还是吞下IOException,都应由以下操作来决定。
FileInputStream fis = null; try { fis = new FileInputStream(file); ... process ... } catch (IOException e) { ... blah blah blah ... } finally { try { if (fis != null) fis.close(); } catch (IOException e) { } }
我们还可以使用一个简单的静态Helper方法:
public static void closeQuietly(InputStream s) { if (null == s) { return; } try { s.close(); } catch (IOException ioe) { //ignore exception } }
并从finally块中使用它。
希望有一天我们会用Java进行闭包,然后我们将失去很多冗长的细节。
因此,取而代之的是在javaIO中将有一个可以导入的辅助方法,它可能需要一个"可关闭"的接口以及一个块。在该辅助方法内,一劳永逸地定义了try {closable.close()} catch(IOException ex){// blah},然后我们就可以编写
Inputstream s = ....; withClosable(s) { //your code here }
除了非常小的风格建议外,没有什么可添加的。在这种情况下,使用了自我记录代码的规范示例,为必须在close()
上捕获的被忽略的IOException
提供了一个描述性变量名。
squiddle的答案变成:
public static void closeQuietly(InputStream s) { try { s.close(); } catch (IOException ignored) { } }
我们主要关心的是从FindBugs获得干净的报告,还是拥有有效的代码?这些不一定是同一回事。原始代码很好(尽管我会摆脱多余的if(fis!= null)
检查,因为否则会抛出OutOfMemoryException
)。 FileInputStream有一个finalizer方法,如果我们在处理过程中实际收到IOException,则该方法将为我们关闭流。避免使代码更加复杂以避免极端不可能的情况是不值得的
- 你得到一个IOException和
- 这种情况经常发生,以至我们开始遇到终结器积压问题。
编辑:如果我们收到太多IOExceptions,以致终结器队列出现问题,那么我们要炸得多的鱼了!这是关于获得远见的感觉。
对于Java 7及更高版本,应使用try-with-resources:
try (InputStream in = new FileInputStream(file)) { // TODO: work } catch (IOException e) { // TODO: handle error }
如果我们坚持使用Java 6或者更低版本...
此模式避免使用null乱码:
try { InputStream in = new FileInputStream(file); try { // TODO: work } finally { in.close(); } } catch (IOException e) { // TODO: error handling }
有关如何有效处理关闭的更多详细信息,请阅读此博客文章:Java:如何避免混乱的流处理。它具有更多示例代码,更多深度,并涵盖了在catch块中进行紧密包装的陷阱。
在大多数情况下,我发现最好不要捕获IO异常,而只需使用try-finally即可:
final InputStream is = ... // (assuming some construction that can't return null) try { // process is ... } finally { is.close(); }
除了FileNotFoundException之外,我们通常无法"解决" IOException。剩下要做的唯一一件事就是报告错误,通常我们将在调用堆栈中进一步处理该错误,因此我发现更好地传播异常。
由于IOException是一个检查过的异常,因此我们必须声明此代码(及其任何客户端)抛出IOException。那可能太吵了,或者我们可能不想透露使用IO的实现细节。在这种情况下,我们可以使用异常处理程序包装整个块,该异常处理程序将" IOException"包装为" RuntimeException"或者抽象异常类型。
详细信息:我知道当finally块中的close操作产生IOException时,上述代码吞噬了try块中的任何异常。我不认为这是个大问题:通常,来自try块的异常将与导致close失败的IOException相同(即,IO正常工作然后失败的情况很少见在结束时)。如果这是一个问题,那么"沉默"关闭可能是值得的麻烦。