代码之家  ›  专栏  ›  技术社区  ›  Student

Java IO丑陋尝试最后块

  •  42
  • Student  · 技术社区  · 16 年前

    有没有一种不那么难看的方法 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();
        }
    

    更新:以上所有代码都在一个Try-Catch中,谢谢您的警告。

    最后(答案之后):

    一个好的实用方法可以用 Execute Around idiom (谢谢汤姆·霍丁)。

    12 回复  |  直到 9 年前
        1
  •  51
  •   Community Mohan Dere    9 年前

    这是正确的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),现在尝试使用资源语法来使这个更干净。如何处理这一点在 this question .

        2
  •  32
  •   OscarRyz    16 年前

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

    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);
    }
    

    附加的

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

        3
  •  18
  •   Community Mohan Dere    9 年前
    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 )

    结果是 catch finally 应该很少分享 尝试 .

    由于Java SE 7,您可以使用尝试资源来避免这么多的缩进。它或多或少地做同样的事情,尽管有抑制异常隐藏起来。

    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 .

        4
  •  8
  •   ColinD    16 年前

    Guava 有非常好的IO API,不需要这样做。例如,您的示例是:

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

    一般来说,它使用 InputSupplier S和 OutputSupplier S允许 InputStream S和 OutputStream 在它的实用方法中创建,允许它完全控制它们,以便它能够正确地处理关闭。

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

    它中的IO内容仍然是beta版本,可能会发生变化,但是它值得检查甚至使用,这取决于你正在做什么。

        5
  •  7
  •   vodkhang    16 年前

    我坚信,在Java 7中,不再需要显式地关闭流。 Language Features in Java 7

    try (BufferedReader br = new BufferedReader(new FileReader(path)) {
       return br.readLine();
    }
    
        6
  •  6
  •   Azri Jamil    9 年前

    自从 爪哇7 有一种更好的方法来编写Try Finally块 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 循环完成,资源将关闭!

        7
  •  5
  •   Istao    16 年前

    下议院的IO IOUtils 一些 封闭地 方法。

        8
  •  2
  •   Stephen C    16 年前

    我有时使用的一个技巧是定义一个方法 closeQuietly(Closeable) 测试它的论点是否 null 然后关闭它,忽略任何异常。但是您需要小心地关闭输出流和编写器,因为它们实际上可能抛出一个异常 事项 ;例如,如果最终冲洗失败。

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

    最后,您应该知道您的示例有一个bug。如果方法调用打开第二个流,则不会关闭第一个流。第二次打开需要在 try 块。

        9
  •  1
  •   leonbloy    16 年前

    在大多数情况下,“in”close()异常是不相关的,因此:

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

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

        10
  •  0
  •   user3402937    12 年前

    使用

    IOUtils.closeNoThrow(myInputStream);

    简单而优雅。

        11
  •  0
  •   Community Mohan Dere    9 年前

    希望这是我的答案 好得多

    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) {}
        }
      } 
    }
    
        12
  •  -2
  •   Val    13 年前

    在C语言中,有 using 当我们离开作用域时自动关闭可关闭对象的构造:

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

    我认为这是Java的Rest-Boad块的一种简短形式。Java 6引入了可关闭的接口。所以, 使用 就快到了。当最后一步在Java 7中完成时,它将是非常棒的。