web-dev-qa-db-ja.com

Javaリソースを取得/閉じる際のtry / catch / finallyベストプラクティス

学校のプロジェクトに取り組んでいる間に、次のコードを書きました。

_FileOutputStream fos;
ObjectOutputStream oos;
try {
    fos = new FileOutputStream(file);
    oos = new ObjectOutputStream(fos);

    oos.writeObject(shapes);
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) oos.close();
    if (fos != null) fos.close();
}
_

問題は、Netbeansがresource.close()行がIOExceptionをスローすることを通知しているため、キャッチまたは宣言する必要があることです。また、oosfosがまだ初期化されていない可能性があることにも不満を感じています(nullチェックにもかかわらず)。

IOExceptionをそこで停止することが全体のポイントであると考えると、これは少し奇妙に思えます。

私のひざの修正はこれを行うことです:

_} finally {
    try {
        if (oos != null) oos.close();
        if (fos != null) fos.close();
    } catch (IOException ex) { }
}
_

しかし、これは私を悩ませ、汚く感じます。

私はusingブロックを単に利用するC#のバックグラウンドから来ているので、これを処理するための「正しい」方法が何であるかはわかりません。

isこの問題を処理する正しい方法は何ですか?

40
Austin Hyde

ソースですべての例外をキャッチして報告しようとしている場合、より良い解決策は次のとおりです。

_ObjectOutputStream oos = null;
try {
   oos = new ObjectOutputStream(new FileOutputStream(file));
   oos.writeObject(shapes);
   oos.flush();
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) {
        try {
            oos.close();
        } catch (IOException ex) {
            // ignore ... any significant errors should already have been
            // reported via an IOException from the final flush.
        }
    }
}
_

ノート:

  • 標準のJavaラッパーストリーム、リーダー、およびライターはすべて、closeおよびflushをラップされたストリームに伝播します。したがって、最も外側を閉じるかフラッシュするだけです。ラッパー。
  • Tryブロックの最後で明示的にフラッシュする目的は、IOExceptionの(実際の)ハンドラーが書き込みエラーを確認できるようにすることです。1
  • 出力ストリームでクローズまたはフラッシュを実行すると、ディスクエラーまたはファイルシステムがいっぱいになったために例外がスローされる可能性があります。 この例外をつぶさないでください!.

「IOExceptionsを無視してnullの可能性があるストリームを閉じる」必要がある場合は、次のようなヘルパーメソッドを自分で作成できます。

_public void closeQuietly(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ex) {
            // ignore
        }
    }
}
_

次に、前のfinallyブロックを次のように置き換えることができます。

_} finally {
    closeQuietly(oos);
}
_

(別の答えは、Apache CommonsライブラリでcloseQuietlyメソッドが既に利用可能であることを指摘しています... 10行のメソッドのプロジェクトに依存関係を追加することを気にしない場合。[〜# 〜] update [〜#〜]:これらのメソッドはAPIバージョン2.6で廃止されることに注意してください。)

ただし、IO例外reallyが無関係であるストリームでのみcloseQuietlyを使用することに注意してください。

1-try-with-resourcesを使用する場合、これは必要ありません。


人々が尋ねているflush()close()の問題について:

  • 標準の「フィルター」および「バッファー付き」出力ストリームおよびライターには、close()によりすべてのバッファー付き出力がフラッシュされることを示すAPIコントラクトがあります。 shouldは、出力バッファリングを行う他のすべての(標準)出力クラスが同じように動作することを発見します。したがって、標準クラスでは、flush()の直前にclose()を呼び出すことは冗長です。
  • カスタムクラスとサードパーティクラスの場合、調査する必要があります(たとえば、javadocを読んでコードを確認します)が、バッファされたデータをフラッシュしないclose()メソッドは間違いなく壊れた
  • 最後に、flush()が実際に行うことの問題があります。 javadocが言うことはこれです(OutputStreamの場合...)

    このストリームの目的の宛先が、基礎となるオペレーティングシステム(ファイルなど)によって提供される抽象化である場合、ストリームをフラッシュすると、以前にストリームに書き込まれたバイトのみが書き込みのためにオペレーティングシステムに渡されます。ディスクドライブなどの物理デバイスに実際に書き込まれることを保証するものではありません。

    だから... flush()を呼び出すことでデータが持続することを保証することを期待/想像するなら、あなたは間違っています!(そのようなことをする必要があるなら、 _FileChannel.force_メソッド...)


一方、Java 7以降を使用できる場合は、@ Mike Clarkの答えで説明されている「新しい」try-with-resourcesが最適なソリューションです。

新しいコードにJava 7以降を使用していない場合は、おそらく深い穴にあり、より深く掘っています。

53
Stephen C

クローズ可能なオブジェクト(ファイルなど)を含むtry/catch/finallyの現在のベストプラクティスは、Java 7のtry-with-resourceステートメント、たとえば:

try (FileReader reader = new FileReader("ex.txt")) {
    System.out.println((char)reader.read());
} catch (IOException ioe) {
    ioe.printStackTrace();
}

この場合、FileReaderはtryステートメントの終わりで自動的に閉じられ、明示的なfinallyブロックで閉じる必要はありません。ここにいくつかの例があります:

http://ppkwok.blogspot.com/2012/11/Java-cafe-2-try-with-resources.html

公式Javaの説明は次のとおりです。

http://docs.Oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html

25
Phil

Java 7は 自動リソース管理 ブロックを追加します。これらはC#のusingに非常に似ています。

Josh Blochが 技術提案 を書いたので、読むことを強くお勧めします。これは、今後のJava 7言語機能に足を踏み入れるためだけでなく、仕様がそのような構成の必要性を動機付けるためであり、そうすることで、正しいコードを書く方法を示しますARMがない場合でも。

ARM形式に変換されたAskerのコードの例を次に示します。

try (FileOutputStream fos = new FileOutputStream(file);
        ObjectOutputStream oos = new ObjectOutputStream(fos)) 
{
    oos.writeObject(shapes);
}
catch (FileNotFoundException ex) 
{
    // handle the file not being found
}
catch (IOException ex) 
{
    // handle some I/O problem
}
13
Mike Clark

私は通常、次のようなメソッドを持つ小さなクラスのIOUtilを持っています。

public static void close(Closeable c) {
    if (c != null) {
        try {
            c.close();
        }
        catch (IOException e) {
            // ignore or log
        }
    }
}
4
maximdim

この男はどうですか? nullチェックなし、驚きなし。すべてが終了時にクリーンアップされます。

try {
    final FileOutputStream fos = new FileOutputStream(file);
    try {
        final ObjectOutputStream oos = new ObjectOutputStream(fos);
        try {
            oos.writeObject(shapes);
            oos.flush();
        }
        catch(IOException ioe) {
            // notify user of important exception
        }
        finally {
            oos.close();
        }
    }
    finally {
        fos.close();
    }
}
catch (FileNotFoundException ex) {
    // complain to user
}
catch (IOException ex) {
    // notify user
}
3
RAY

残念ながら、言語レベルのサポートはありません。しかし、これを簡単にする多くのライブラリがあります。 commons-ioライブラリを確認してください。または、最新のgoogle-guava @ http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html

1

あなたはそれを正しくやっています。それは私もがらくたを悩ます。これらのストリームを明示的にnullに初期化する必要があります-これは一般的な規則です。できることは、クラブに参加してusingが欲しいだけです。

1
Mike

あなたのポイントへの直接的な答えではありませんが、finallycatchは両方ともtryに関連付けられているので、人々は彼らが一緒にいると思います。 tryブロックの最適な設計は、catchまたはfinallyのいずれかを持ち、両方は持たないことです。

この場合、コメントは何かが間違っていることを示唆しています。ファイルIOを扱うメソッドで、ユーザーに何か不満を言うのはなぜですか。ユーザーが見えない場所でサーバーを深く実行している可能性があります。

したがって、上記のコードには、問題が発生したときに正常に失敗するようにfinallyが必要です。ただし、エラーをインテリジェントに処理する能力が不足しているため、catchはコールチェーンの上位に属します。

0
CurtainDog