101

Is there an elegant way to handle exceptions that are thrown in finally block?

For example:

try {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
finally {
   try{
     resource.close();
   }
   catch( Exception ex ) {
     // Could not close the resource?
   }
}

How do you avoid the try/catch in the finally block?

4

15 に答える 15

73

私は通常、次のようにします。

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 );
  }
}
于 2009-01-26T21:55:35.347 に答える
26

私は通常、次のいずれかのcloseQuietly方法を使用しorg.apache.commons.io.IOUtilsます。

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}
于 2009-01-26T23:16:37.680 に答える
22

If you're using Java 7, and resource implements AutoClosable, you can do this (using InputStream as an example):

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
于 2012-02-09T06:55:52.443 に答える
8

間違いなく少しやりすぎですが、例外が発生し、メソッド内から何もログに記録できない場合 (たとえば、ライブラリであり、呼び出し元のコードに例外とログを処理させたい場合) に役立つ可能性があります。

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 つにまとめました。

于 2011-06-14T08:57:23.683 に答える
6

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 メソッドが呼び出され、次に Statement が呼び出され、最後に Connection オブジェクトに対して呼び出されることを意味します。

また、close メソッドが自動的に呼び出されたときに発生する例外はすべて抑制されることを知っておくことも重要です。これらの抑制された例外は、 Throwableクラスで定義されたgetsuppressed()メソッドによって取得できます。

ソース: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

于 2015-04-09T16:06:02.077 に答える
3

'finally'ブロックで発生する例外を無視することは、それらの例外がどのようなものであり、それらがどのような条件を表すかを知らない限り、一般的に悪い考えです。通常のtry/finally使用パターンでは、tryブロックは物事を外部コードが予期しない状態に置き、finallyブロックはそれらの物事の状態を外部コードが期待する状態に復元します。例外をキャッチする外部コードは、通常、例外にもかかわらず、すべてが復元されていることを期待します。normal州。たとえば、あるコードがトランザクションを開始してから、2つのレコードを追加しようとしたとします。「finally」ブロックは、「コミットされていない場合はロールバック」操作を実行します。呼び出し元は、2番目の「追加」操作の実行中に例外が発生する準備ができている可能性があり、そのような例外をキャッチすると、データベースはいずれかの操作が試行される前の状態になると予想される場合があります。ただし、ロールバック中に2番目の例外が発生した場合、呼び出し元がデータベースの状態について何らかの仮定を行うと、問題が発生する可能性があります。ロールバックの失敗は大きな危機を表しています。これは、単なる「レコードの追加に失敗しました」という例外を予期するコードによって捕らえられるべきではありません。

私の個人的な傾向は、finallyメソッドで発生した例外をキャッチし、それらを「CleanupFailedException」でラップすることです。このような失敗は大きな問題であり、このような例外を軽視してはならないことを認識しています。

于 2012-02-09T20:45:01.193 に答える
2

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 */ }
 }
于 2009-01-26T21:35:11.753 に答える
1

多くの検討の結果、次のコードが最適であることがわかりました。

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

そのコードは以下を保証します:

  1. コードが終了すると、リソースは解放されます
  2. リソースを閉じるときにスローされた例外は、それらを処理せずに消費されません。
  3. コードはリソースを 2 回閉じようとしないため、不要な例外は作成されません。
于 2012-01-17T13:36:50.850 に答える
1

なぜ追加のブロックを避けたいのですか? 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) {
    ...
}
于 2009-01-26T21:42:03.377 に答える
0

私は通常これを行います:

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}

理論的根拠:リソースを使い終わって、リソースを閉じることが唯一の問題である場合、それについてできることはあまりありません。とにかくリソースを使い終わった場合、スレッド全体を強制終了することも意味がありません。

これは、少なくとも私にとっては、チェックされた例外を無視しても安全な場合の1つです。

今日まで、私はこのイディオムを使用することに何の問題もありませんでした。

于 2009-01-26T22:40:58.267 に答える
0

これを別の方法にリファクタリングできます...

public void RealDoSuff()
{
   try
   { DoStuff(); }
   catch
   { // resource.close failed or something really weird is going on 
     // like an OutOfMemoryException 
   }
}

private void DoStuff() 
{
  try 
  {}
  catch
  {
  }
  finally 
  {
    if (resource != null) 
    {
      resource.close(); 
    }
  }
}
于 2009-01-26T21:50:30.210 に答える
0
try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

ジョブ完了。null テストはありません。シングル キャッチ、例外の取得と解放を含みます。もちろん、Execute Around イディオムを使用して、リソースの種類ごとに 1 回記述するだけで済みます。

于 2009-01-27T13:50:57.633 に答える
0

可能であれば、最初からエラー状態を回避するためにテストする必要があります。

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 ブロックで囲む必要があります (ただし、特定の予想されるエラーを引き続きキャッチすることをお勧めします)。

于 2009-01-26T21:39:29.360 に答える