回答:
私は通常、次のようにします。
try {
// Use the resource.
} catch( Exception ex ) {
// Problem with the resource.
} finally {
// Put away the resource.
closeQuietly( resource );
}
その他:
protected void closeQuietly( Resource resource ) {
try {
if (resource != null) {
resource.close();
}
} catch( Exception ex ) {
log( "Exception during Resource.close()", ex );
}
}
Resource
=> Closeable
?
私は通常、次のいずれかのcloseQuietly
方法を使用しますorg.apache.commons.io.IOUtils
。
public static void closeQuietly(OutputStream output) {
try {
if (output != null) {
output.close();
}
} catch (IOException ioe) {
// ignore
}
}
おそらく少し上ですが、例外をバブルアップさせてメソッド内から何もログに記録できない場合に役立つかもしれません(たとえば、それがライブラリであり、呼び出しコードに例外とロギングを処理させたい場合)。
Resource resource = null;
boolean isSuccess = false;
try {
resource = Resource.create();
resource.use();
// Following line will only run if nothing above threw an exception.
isSuccess = true;
} finally {
if (resource != null) {
if (isSuccess) {
// let close throw the exception so it isn't swallowed.
resource.close();
} else {
try {
resource.close();
} catch (ResourceException ignore) {
// Just swallow this one because you don't want it
// to replace the one that came first (thrown above).
}
}
}
}
更新:私はこれをもう少し調べて、私よりもこれについて明確に考えている誰かからの素晴らしいブログ投稿を見つけました:http : //illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make -mess-of-stream.html 彼はさらに一歩進んで、2つの例外を1つに結合しました。
ignore
例外をログに記録します
Java 7以降では、finallyブロックでリソースを明示的に閉じる必要がなくなり、代わりにtry -with-resources構文を使用できます。try-with-resourcesステートメントは、1つ以上のリソースを宣言するtryステートメントです。リソースは、プログラムが終了した後に閉じる必要があるオブジェクトです。try-with-resourcesステートメントは、各リソースがステートメントの最後で確実に閉じられるようにします。java.io.Closeableを実装するすべてのオブジェクトを含む、java.lang.AutoCloseableを実装するすべてのオブジェクトをリソースとして使用できます。
次のコードを想定します。
try( Connection con = null;
Statement stmt = con.createStatement();
Result rs= stmt.executeQuery(QUERY);)
{
count = rs.getInt(1);
}
例外が発生した場合、3つのリソースそれぞれに対して、作成された順序とは逆の順序でcloseメソッドが呼び出されます。つまり、最初にResultSetmに対してcloseメソッドが呼び出され、次にステートメントが呼び出され、最後にConnectionオブジェクトに対して呼び出されます。
また、closeメソッドが自動的に呼び出されたときに発生する例外が抑制されることを理解することも重要です。これらの抑制された例外は、Throwableクラスで定義されたgetsuppressed()メソッドによって取得できます。
ソース:https : //docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
「最終的に」ブロックで発生する例外を無視することは、それらの例外が何であり、それらが表す条件を知らない限り、一般に悪い考えです。通常のtry/finally
使用パターンでは、try
ブロックは外部コードが予期しない状態に物事を置き、ブロックは外部コードが予期finally
するものにそれらの物事の状態を復元します。例外をキャッチする外部コードでは、例外にもかかわらず、すべてがnormal
状態。たとえば、あるコードがトランザクションを開始してから、2つのレコードを追加しようとしたとします。「最終的に」ブロックは「コミットされていない場合はロールバック」操作を実行します。呼び出し元は、2番目の「追加」操作の実行中に例外が発生する準備ができている可能性があり、そのような例外をキャッチした場合、データベースはいずれかの操作が試行される前の状態になると期待できます。ただし、ロールバック中に2番目の例外が発生した場合、呼び出し元がデータベースの状態について何らかの仮定を行うと、問題が発生する可能性があります。ロールバックの失敗は、重大な危機を表しています。単なる「レコードの追加に失敗しました」という例外を予期しているコードに捕まってはなりません。
私の個人的な傾向は、finallyメソッドで発生した例外をキャッチして「CleanupFailedException」にラップすることです。このような失敗は大きな問題を表し、そのような例外は軽くキャッチされるべきではないことを認識しています。
2つの例外が2つの異なるクラスである場合の1つの解決策
try {
...
}
catch(package1.Exception err)
{
...
}
catch(package2.Exception err)
{
...
}
finally
{
}
しかし、この2回目のtry-catchを回避できない場合があります。たとえば、ストリームを閉じるため
InputStream in=null;
try
{
in= new FileInputStream("File.txt");
(..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
}
catch(SQLException err)
{
//handle exception
}
finally
{
//at the end, we close the file
if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
}
追加のブロックを避けたいのはなぜですか?finallyブロックには、例外をスローする可能性のある「通常の」操作が含まれているため、finallyブロックを完全に実行するには、例外をキャッチする必要があります。
finallyブロックが例外をスローすることを期待せず、とにかく例外を処理する方法がわからない場合(スタックトレースをダンプするだけです)、例外を呼び出しスタックにバブルアップさせます(finallyからtry-catchを削除します)ブロック)。
タイプを減らしたい場合は、「グローバルな」外側のtry-catchブロックを実装できます。これにより、finallyブロックでスローされたすべての例外がキャッチされます。
try {
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
try {
...
} catch (Exception ex) {
...
} finally {
...
}
} catch (Exception ex) {
...
}
多くの検討の結果、次のコードが最適であることがわかりました。
MyResource resource = null;
try {
resource = new MyResource();
resource.doSomethingFancy();
resource.close();
resource = null;
} finally {
closeQuietly(resource)
}
void closeQuietly(MyResource a) {
if (a!=null)
try {
a.close();
} catch (Exception e) {
//ignore
}
}
そのコードは以下を保証します:
可能であれば、最初にエラー状態を回避するためにテストする必要があります。
try{...}
catch(NullArgumentException nae){...}
finally
{
//or if resource had some useful function that tells you its open use that
if (resource != null)
{
resource.Close();
resource = null;//just to be explicit about it was closed
}
}
また、回復できる例外のみをキャッチする必要があります。回復できない場合は、プログラムのトップレベルに伝播させます。エラー条件をテストできない場合は、既に行ったようにコードをtry catchブロックで囲む必要があります(ただし、特定の予期されるエラーをキャッチすることをお勧めします)。
私は通常これを行います:
MyResource r = null;
try {
// use resource
} finally {
if( r != null ) try {
r.close();
} catch( ThatSpecificExceptionOnClose teoc ){}
}
理論的根拠:リソースを使い終わって、唯一の問題がリソースを閉じることである場合、それについてできることはあまりありません。とにかくリソースを使い終わった場合、スレッド全体を強制終了することも意味がありません。
これは、少なくとも私にとっては、そのチェックされた例外を無視しても安全な場合の1つです。
今日まで、私はこのイディオムを使用して問題を抱えていませんでした。
try {
final Resource resource = acquire();
try {
use(resource);
} finally {
resource.release();
}
} catch (ResourceException exx) {
... sensible code ...
}
仕事が終わりました。nullテストはありません。単一のキャッチ。取得および解放の例外を含みます。もちろん、Execute Aroundイディオムを使用でき、リソースタイプごとに1回だけ記述する必要があります。
リソースでtryを使用できない同様の状況に遭遇しましたが、closeQuietlyメカニズムのようにログに記録して無視するだけでなく、closeからの例外も処理したいと思いました。私の場合、実際には出力ストリームを処理していないため、単純なストリームよりもクローズ時の失敗の方が重要です。
IOException ioException = null;
try {
outputStream.write("Something");
outputStream.flush();
} catch (IOException e) {
throw new ExportException("Unable to write to response stream", e);
}
finally {
try {
outputStream.close();
} catch (IOException e) {
ioException = e;
}
}
if (ioException != null) {
throw new ExportException("Unable to close outputstream", ioException);
}