2008-10-01 226 views
30

好吧,我一直在做如下(变量名称已更改):关闭一个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库引发了太多的检查异常。

+0

`fis`不可能在你测试的时候为空。它可以在缺失的`finally`块中为null,在那里你应该测试它并关闭它。但是自引入“试用资源”语法以来,这个问题已经过时了。 – EJP 2015-04-28 01:14:58

+0

我修改了相应的代码,以免人们误导。 – 2015-04-28 14:23:57

回答

38

对于Java 7和以上try-with-resources应使用:

try (InputStream in = new FileInputStream(file)) { 
    // TODO: work 
} catch (IOException e) { 
    // TODO: handle error 
} 

如果你被困在Java 6或以下...

这种模式避免了与碴围:

try { 
     InputStream in = new FileInputStream(file); 
     try { 
      // TODO: work 
     } finally { 
      in.close(); 
     } 
    } catch (IOException e) { 
     // TODO: error handling 
    } 

对于更详细的关于如何有效地与密切处理,阅读这篇博客:Java: how not to make a mess of stream handling。它有更多的样本代码,更多的深度和覆盖关闭捕获块的陷阱。

25

像下面这样的东西应该做到这一点,不管你在试图关闭流时是抛出还是吞下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) 
    { 
    } 
} 
4

你也可以使用一个简单的静态辅助方法:

public static void closeQuietly(InputStream s) { 
    if (null == s) { 
     return; 
    } 
    try { 
     s.close(); 
    } catch (IOException ioe) { 
     //ignore exception 
    } 
} 

,并使用该从你的finally块。

+0

那么,这看起来像邀请NullPointerException ... – 2008-10-01 07:22:48

+0

感谢您的评论,相应地改变了它 – squiddle 2008-10-07 15:35:58

0

希望有一天我们会在Java中关闭,然后我们将失去很多冗长。

所以相反,在javaIO中会有一个可以导入的辅助方法,它可能需要一个“Closable”接口和一个块。里面是辅助方法尝试{closable.close()}赶上(IOException异常前){//等等}被定义一劳永逸,然后你就可以写

Inputstream s = ....; 
withClosable(s) { 
    //your code here 
} 
3

没什么补充,除了一个非常小的文体建议。 自编文件代码的典型示例适用于这种情况 - 为忽略的IOException提供一个描述性变量名称,您必须在close()上记录该变量名称。

所以squiddle的回答变成了:

public static void closeQuietly(InputStream s) { 
    try { 
     s.close(); 
    } catch (IOException ignored) { 
    } 
} 
-4

你关心的主要是从获得的FindBugs或与具有工作代码干净的报告?这些不一定是一回事。你的原始代码是好的(尽管我会摆脱多余的if (fis != null)检查,因为否则将会抛出OutOfMemoryException)。 FileInputStream有一个终结器方法,它会在你处理中实际收到IOException的情况下为你关闭流。它只是不值得费心使代码更复杂,以避免你得到一个IOException和

  • 这种情况经常是你开始运行到终结积压问题

    1. 极端情况不太可能发生的。

    编辑:如果你得到这么多的IOExceptions您正在运行与终结队列问题,那么你已经远远更大的鱼鱼苗!这是关于获得一种观点。

  • 2

    在大多数情况下,我发现这是只有更好赶上IO异常,只需使用try-最后:

    final InputStream is = ... // (assuming some construction that can't return null) 
    try { 
        // process is 
        ... 
    } finally { 
        is.close(); 
    } 
    

    除了FileNotFoundException,你一般不能“解决”一个IOException。剩下的唯一要做的就是报告一个错误,并且通常会在调用堆栈中进一步处理,所以我发现传播异常更好。

    由于IOException是一个检查异常,您将不得不声明此代码(及其任何客户端)throws IOException。这可能太吵了,或者你可能不想透露使用IO的实现细节。在这种情况下,可以用一个异常处理程序来包装整个块,该异常处理程序将IOException包含在RuntimeException或抽象异常类型中。

    详情:我知道的是,上述代码吞下从try块任何异常时在finally块的close操作产生IOException。我不认为这是一个大问题:通常,try块的例外情况与IOException的例外情况相同,导致close失败(即,IO很难正常工作,然后在关闭时失败) 。如果这是一个问题,那么可能需要麻烦来“平息”收盘价。

    1

    以下解决方案在关闭失败时正确引发异常,而不会在关闭前隐藏可能的异常。

    try { 
        InputStream in = new FileInputStream(file); 
        try { 
         // work 
         in.close(); 
        } finally { 
         Closeables.closeQuietly(in); 
        } 
    } catch(IOException exc) { 
        // kernel panic 
    } 
    

    这是可行的,因为第二次关闭has no effect

    这依靠番石榴Closeables,但如果喜欢,可以编写自己的closeQuietly方法,如squiddle(另请参阅serg10)所示。

    在一般情况下报告关闭错误很重要,因为close可能会向流中写入一些最终字节,例如,因为缓冲。因此,您的用户想知道它是否失败,或者您可能想采取某种行动。当然,这在FileInputStream的特定情况下可能不是真的,但我不知道(但由于已经提到的原因,我认为最好报告一个关闭错误,如果它发生的话)。

    由于嵌入式try块的结构,上面的代码有点难以理解。它可能被认为是更清晰的两个方法,一个抛出一个IOException和一个捕获它的方法。至少这是我会选择的。

    private void work() throws IOException { 
        InputStream in = new FileInputStream(file); 
        try { 
         // work 
         in.close(); 
        } finally { 
         Closeables.closeQuietly(in); 
        } 
    } 
    
    public void workAndDealWithException() { 
        try { 
         work(); 
        } catch(IOException exc) { 
         // kernel panic 
        } 
    } 
    

    基于http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html(由McDowell引用)。

    10

    您可以使用添加了JDK7的try-with-resources功能。正是建立应对这样的事情

    static String readFirstLineFromFile(String path) throws IOException { 
        try (BufferedReader br = new BufferedReader(new FileReader(path))) { 
        return br.readLine(); 
        } 
    } 
    

    的documenation说:

    试戴与资源语句确保每个资源在发言结束时关闭 。