web-dev-qa-db-ja.com

Java io ugly try-finally block

両方のストリームを閉じるためにclose()例外を扱うあまりsoくない方法はありますか:

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

更新:上記のコードはすべて、警告のおかげでもう1つのtry-catch内にあります。

最後に(答えの後):

そして、 Execute Around idiom (Tom Hawtinに感謝)を使用して、優れたユーティリティメソッドを実行できます。

43
Tom Brito

これは正しい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
     }
  }

これがうまく機能する理由は、finallyコードが例外をスローしたり、異常終了したりしない限り、finallyに到達する前にスローされた例外が、finallyコードの終了後にスローされるためです。

編集:Java 7(およびAndroid SDK 19-KitKat)の時点で、このクリーナーを作成するためのTry with resources構文があります。対処方法 この質問 で対処されています。

54
Yishai

ユーティリティメソッドを実装できます。

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

追加

サードパーティのオープンソースライブラリにこのような方法があると思います。ただし、私の機能は、その機能の大部分を使用していない限り、不要なライブラリの依存関係を回避することです。したがって、私はこのような単純なユーティリティメソッドを自分で実装する傾向があります。

32
Adamski
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が必要になることに注意してください(nullsに関連するハックは行わないでください。ErrorExceptionのインスタンスではありません).

catchfinallyが同じ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);
}

Execute Around idiom を使用することもできます。

コピーする標準的な良い方法は、NIOのtransferTo/transferFromを使用することだと思います。

18

Guava には非常に素晴らしいIOこれが不要なAPIがあります。たとえば、あなたの例は:

_Files.copy(new File(inputFileName), new File(outputFileName));
_

より一般的には、InputSuppliersおよびOutputSuppliersの概念を使用して、そのユーティリティメソッド内でInputStreamsおよびOutputStreamsを作成できるようにし、完全に制御できるようにします。クローズを適切に処理できるようにします。

さらに、Closeables.closeQuietly(Closeable)があります。これは、基本的にほとんどの答えが示唆しているメソッドのタイプです。

その中のIOのものはまだベータ版であり、変更される可能性がありますが、作業している内容によっては、チェックアウトして使用する価値があります。

8
ColinD

Java 7.0では、ストリームを自分で明示的に閉じる必要はもうありません。 Language Features in Java 7

try (BufferedReader br = new BufferedReader(new FileReader(path)) {
   return br.readLine();
}
7
vodkhang

Java 7なので、Closeableリソースに関してtry-finallyブロックを書くためのより良い方法があります。 。

次のように、tryキーワードの後に​​括弧でリソースを作成できます。

try (initialize resources here) {
   ...
}

そして、コードのブロックが終了すると、それらは閉じられますautomaticallyfinally部分は必要ありません。

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
darijan

Commons ioには IOUtils 、いくつかのcloseQuietlyメソッドがあります。

5
Istao

私が時々使うトリックの1つは、引数がnullかどうかをテストしてから例外を無視してcloseQuietly(Closeable)というメソッドを定義することです。ただし、実際にはmatters;の例外をスローする可能性があるため、OutputStreamsおよびWritersを慎重に閉じる必要があります。例えば最終フラッシュが失敗した場合。

Java 7.で改善される可能性があります。レポートには、管理対象リソースを処理するより簡潔な方法を提供する新しい構成が含まれます。 。

最後に、例にバグがあることに注意する必要があります。メソッド呼び出しが2番目のストリームを開く場合、最初のストリームは閉じられません。 2番目のオープンは、tryブロック内で行う必要があります。

2
Stephen C

ほとんどの場合、 '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 */ }
    } 

例外を飲み込むことは通常悪い習慣ですが、この場合は大丈夫だと思います。

1
leonbloy

うまくいけば私の答えははるかに良いです

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

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) {}
    }
  } 
}
0
Noor Nawaz

使用する

IOUtils.closeNoThrow(myInputStream);

シンプルでエレガント。

0
user3402937