2010-04-23 58 views
40

是否有治疗close()例外,关闭两个流,那么的不那么丑陋的方式:的Java IO丑陋的try-finally块

InputStream in = new FileInputStream(inputFileName); 
    OutputStream out = new FileOutputStream(outputFileName); 

    try { 
     copy(in, out); 
    } finally { 
     try { 
      in.close(); 
     } catch (Exception e) { 
      try { 
       // event if in.close fails, need to close the out 
       out.close(); 
      } catch (Exception e2) {} 
       throw e; // and throw the 'in' exception 
      } 
     } 
     out.close(); 
    } 

更新:所有上面的代码是一个尝试,渔获物中,由于为警告。

FINALLY(后回答):

,并可以使用Execute Around idiom(感谢汤姆Hawtin)做一个很好的实用方法。

+6

考虑在抽象的适当水平捕获异常,例如IOException等,而不是过于模糊的异常。 – JRL 2010-04-23 14:20:02

+1

我认为对于这种情况,抛出哪个异常无关紧要。或者为什么会呢? – 2010-04-23 17:02:09

+3

请注意,您的代码不一定关闭'in',请参阅http://stackoverflow.com/questions/2441853/java-resource-management-please-help-to-understand-findbugs-results – meriton 2010-04-27 21:11:51

回答

46

这是正确的IDOM(它工作正常):

InputStream in = null; 
    OutputStream out = null; 
    try { 
     in = new FileInputStream(inputFileName); 
     out = new FileOutputStream(outputFileName); 
     copy(in, out); 
    finally { 
     close(in); 
     close(out); 
    } 

    public static void close(Closeable c) { 
    if (c == null) return; 
    try { 
     c.close(); 
    } catch (IOException e) { 
     //log the exception 
    } 
    } 

能正常工作的原因是,抛出异常你得终于将你的最终代码完成后抛出之前,前提是你的最后代码本身不会抛出异常或以其他方式异常终止。

编辑:从Java 7(和Android SDK 19 - KitKat)开始,现在有一个Try with resources语法来使这个更清晰。如何处理这个问题在this question

+5

注意:这不是所有流类型的正确习惯用法。如果'OutputStream'缓冲数据('BufferedOutputStream')或写入结束块('ZipOutputStream'),则可能会丢失数据,并且应用程序无法处理它,因为它会吞服异常。记录不能替代正确的错误处理。 – McDowell 2010-06-15 12:09:53

+0

@McDowell,关于输出流是一个很好的观点(请参阅我对Adamski的回答所作的评论),但我仍然会说这是标准习惯用法。 – Yishai 2010-06-15 15:04:11

+0

由于这是公认的答案,人们会看它 - 既然您同意@McDowell的请求,请在您的回答中提及他的评论 - - 在catch块 – 2013-05-02 21:16:26

2

我有时使用的一个技巧是定义一个名为closeQuietly(Closeable)的方法,该方法测试它的参数是否为null然后关闭它,忽略任何异常。但是你需要小心地关闭OutputStreams和Writers,因为它们实际上可能会抛出异常,很重要;例如如果最终刷新失败。

随着Java 7的发展,情况可能会有所改善。报告指出,它将有一个新的构造,它提供了一种更简洁的方式来处理托管资源;例如需要在完成时关闭的流。

最后,你应该知道你的例子有一个错误。如果方法调用打开第二个流,则第一个流将不会关闭。第二次打开需要在try区块内完成。

31

你可以实现一个实用方法:

public final class IOUtil { 
    private IOUtil() {} 

    public static void closeQuietly(Closeable... closeables) { 
    for (Closeable c : closeables) { 
     if (c != null) try { 
      c.close(); 
     } catch(Exception ex) {} 
    } 
    } 
} 

那么你的代码将被简化为:

try { 
    copy(in, out); 
} finally { 
    IOUtil.closeQuietly(in, out); 
} 

附加

我想象还会有这样的方法在第三方开源库中。但是,我的首选是避免不必要的库依赖项,除非我使用其大部分功能。因此我倾向于自己实现这样的简单实用方法。

+1

这有点不同,因为例外情况不会重新发生 – 2010-04-23 14:19:46

+0

@Sam:这是一个好点;我会修改我的答案。 – Adamski 2010-04-23 14:20:43

+0

不错的主意,一个可变关系的var-arg。 – Yishai 2010-04-23 14:21:51

1

在大多数情况下,“在” close()方法的例外是不相关的,因此:

try { 
     copy(in, out); 
    } finally { 
    try { in.close() } catch (Exception e) { /* perhaps log it */ } 
    try { out.close() } catch (Exception e) {/* perhaps log it */ } 
    } 

它通常是不好的做法,吞下例外,但在这种情况下,我认为这是好的。

+0

为什么要忽略写出失败的结尾该文件可以吗? – 2011-01-29 14:32:13

+0

因为你知道这是一个ByteArrayOutputStream? – bmargulies 2012-01-17 11:30:44

16
try { 
    final InputStream in = new FileInputStream(inputFileName); 
    try { 
     final OutputStream out = new FileOutputStream(outputFileName);  
     try { 
      copy(in, out); 
      out.flush(); // Doesn't actually do anything in this specific case. 
     } finally { 
      out.close(); 
     } 
    } finally { 
     in.close(); 
    } 
} catch (IOException exc) { 
    throw new SomeRelevantException(exc); 
} 

记住,打开一个流可能会抛出一个异常,那么你就需要气流出口之间的try(请不要做一些黑客涉及null秒。任何事情都有可能引发Error(这不是一个实例的Exception)。

事实证明,catchfinally应该很少共享相同的try

由于Java SE 7,你可以写使用try-与资源以避免这么多的缺口,这或多或少做同样的事情,虽然有抑制异常隐藏。

try (
    final InputStream in = new FileInputStream(inputFileName); 
    final OutputStream out = new FileOutputStream(outputFileName);  
) { 
    copy(in, out); 
    out.flush(); // Doesn't actually do anything in this specific case. 
} catch (IOException exc) { 
    throw new SomeRelevantException(exc); 
} 

您可能想要使用Execute Around idiom

我相信标准的好方法是使用NIO的transferTo/transferFrom

+0

+1,这与我输入的答案完全相同(character-for-character)! :) – 2010-04-23 14:21:32

+0

实际上很不错 - 请您评论它的好处与接受的答案相比吗?同样,如果说'copy(in,out);'和'out.close();'这两个throw都不会丢失'copy(in,out);''抛出的异常。 – 2013-05-02 23:39:29

+0

@Mr_and_Mrs_D种类。在实践中,未缓冲的I/O流不会从'close'抛出'IOException'。即使他们抛出了'IOException',控制流也是一样的。/Java SE 7增加了抑制异常。我不希望任何人通过抑制树搜索找到正确的异常,这在本地没有理论上可行,因为没有定义异常优先级的顺序。 – 2013-05-03 10:16:54

5

你有,在普通的io,在IOUtils,一些close平静方法。

8

Guava有非常好的IO API,消除这种需要。例如,你的例子是:

Files.copy(new File(inputFileName), new File(outputFileName)); 

更一般地,它使用的InputSupplier S和OutputSupplier S上的概念,让InputStream S和OutputStream s到被它的实用方法中创建,允许它在他们完全控制所以它可以正确处理关闭。

此外,它有Closeables.closeQuietly(Closeable)这基本上是大多数答案建议的方法的类型。

其中的IO内容仍处于测试阶段,可能会发生变化,但值得检查甚至使用,具体取决于您的工作内容。

7

我坚信在Java 7.0中,您不需要再自己关闭流。 Language Features in Java 7

try (BufferedReader br = new BufferedReader(new FileReader(path)) { 
    return br.readLine(); 
} 
+0

当它最终被释放时,这将会非常棒。您可以在try(...)参数中指定多个资源,JVM可以为您关闭这些资源。 – 2010-04-23 17:18:40

+3

虽然如果'BufferedReader'构造函数失败(不太可能,但确实发生),你将会泄漏。你也正在挑选随机混杂的字符编码。 – 2011-01-29 14:36:30

-1

在C#中,有using建设会自动关闭关闭的对象,当我们离开的范围:

using(Stream s = new Stream(filename)) { 
    s.read(); 
} 

我认为这是对Java的尝试-finally块的简写形式。 Java 6引入了Closable接口。所以,using几乎就在那里。当最后一步在Java 7中完成时,确实会非常棒。

0

使用

IOUtils.closeNoThrow(myInputStream);

古朴典雅。

+2

可能你的意思是[IOUtils.closeQuietly](http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/IOUtils.html#closeQuietly(java.io.InputStream))? – 2014-09-17 22:17:01

6

由于的Java 7有写尝试,终于在问候块来Closeable资源方面更好的方式。现在

可以在try关键字后括号内创建你的资源,像这样:

try (initialize resources here) { 
    ... 
} 

与上面的代码块结束后,他们将被关闭自动finally部分不需要。

一个例子

try (
    ZipFile zf = new ZipFile(zipFileName); 
    BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset); 
) { 
    // Enumerate each entry 
    for (Enumeration entries = zf.entries(); entries.hasMoreElements();) { 
     // Get the entry name and write it to the output file 
     String newLine = System.getProperty("line.separator"); 
     String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; 
     writer.write(zipEntryName, 0, zipEntryName.length()); 
    } 
} 

而且for循环完成后,资源将被关闭!

0

这里是我的回答希望更好

https://stackoverflow.com/a/35623998/2585433

try { 
    fos = new FileOutputStream(new File("...")); 
    bos = new BufferedOutputStream(fos); 
    oos = new ObjectOutputStream(bos); 
} 
catch (Exception e) { 
} 
finally { 
    Stream.close(oos,bos,fos); 
} 


class Stream { 

public static void close(AutoCloseable... array) { 
    for (AutoCloseable c : array) { 
     try {c.close();} 
     catch (IOException e) {} 
     catch (Exception e) {} 
    } 
    } 
}