Java io丑陋的try-finally块

Tom*_*ito 43 java file-io try-catch

是否有一种不那么丑陋的方式来处理close()关闭两个流的异常:

    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();
    }
Run Code Online (Sandbox Code Playgroud)

更新:所有上述代码都在一个try-catch内,感谢警告.

最后(在答案之后):

一个好的实用方法可以使用Execute Around成语(感谢Tom Hawtin).

Yis*_*hai 54

这是正确的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
     }
  }
Run Code Online (Sandbox Code Playgroud)

这个工作正常的原因是,在你的finally代码完成之后抛出之前抛出的异常将被抛出,前提是你的finally代码本身不会抛出异常或以其他方式异常终止.

编辑:从Java 7(和Android SDK 19 - KitKat)开始,现在有了一个Try with resources语法来使它更清晰.如何处理这个问题这个问题中得到了解决.

  • 注意:这不是所有流类型的正确习惯用法.如果`OutputStream`缓冲数据(`BufferedOutputStream`)或它写入关闭块(`ZipOutputStream`),您可能会丢失数据,应用程序将无法处理它,因为它吞下了异常.记录不是正确错误处理的替代. (6认同)

Ada*_*ski 32

您可以实现一个实用工具方法:

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) {}
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

那么你的代码将简化为:

try {
  copy(in, out);
} finally {
  IOUtil.closeQuietly(in, out);
}
Run Code Online (Sandbox Code Playgroud)

额外

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

  • 我要添加一个nullcheck.可能尚未分配可关闭资源.对OP:Commons IO库有类似的方法:http://commons.apache.org/io/api-1.4/org/apache/commons/io/IOUtils.html (7认同)
  • 如果只是被忽略,为什么Java开发人员会引发异常呢? (6认同)
  • 你想"静静地"关闭输出流?! (3认同)

Tom*_*ine 18

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);
}
Run Code Online (Sandbox Code Playgroud)

请记住,打开一个流可能会抛出一个异常,所以你确实需要try在流开头之间(请不要做一些涉及nulls的黑客攻击.任何东西都可以抛出Error(这不是一个实例Exception).

事实证明,catch并且finally应该很少共享相同的内容try.

从Java SE 7开始,您可以编写使用try-with-resource来避免这么多缩进.尽管隐藏了被抑制的异常,它或多或少都会做同样的事情.

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);
}
Run Code Online (Sandbox Code Playgroud)

您可能想要使用Execute Around成语.

我认为复制的标准好方法是使用NIO的transferTo/ transferFrom.


Col*_*inD 8

Guava拥有非常好的IO API,无需使用它.例如,您的示例是:

Files.copy(new File(inputFileName), new File(outputFileName));
Run Code Online (Sandbox Code Playgroud)

更一般地说,它使用InputSuppliers和OutputSuppliers 的概念来允许InputStreams和OutputStreams在其实用方法中创建,允许它完全控制它们,以便它可以正确处理关闭.

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

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


vod*_*ang 7

我坚信在Java 7.0中,您不需要自己显式关闭流.Java 7中的语言功能

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然如果`BufferedReader`构造函数失败(不太可能但确实发生),你会泄漏.你也正在接受随机配置的字符编码. (3认同)

dar*_*jan 7

Java 7开始,有一种更好的方法可以在Closeable资源方面编写try-finally块.

现在,您可以在try关键字后的括号中创建资源,如下所示:

try (initialize resources here) {
   ...
}
Run Code Online (Sandbox Code Playgroud)

并且在代码块完成后它们将自动关闭.这部分没有必要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());
    }
}
Run Code Online (Sandbox Code Playgroud)

而经过for循环完成,资源将被关闭!


Ist*_*tao 5

在公共场所,你在IOUtils中有一些closeQuietly方法.