47

次のようなコードのトラブルシューティングで、かなり苦痛なトラブルシューティングの経験がありました。

try {
   doSomeStuff()
   doMore()
} finally {
   doSomeOtherStuff()
}

doSomeStuff() が例外をスローし、それが doSomeOtherStuff() も例外をスローする原因となったため、この問題のトラブルシューティングは困難でした。2 番目の例外 (finally ブロックによってスローされた) がコードにスローされましたが、最初の例外 (doSomeStuff() からスローされた) のハンドルがありませんでした。これが問題の本当の根本原因でした。

コードが代わりにこれを言っていたら、問題はすぐに明らかになったでしょう:

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

だから、私の質問はこれです:

catch ブロックなしで使用される finally ブロックは、よく知られている Java アンチパターンですか? (これは確かに、明らかによく知られているアンチパターン「例外をゴブリングしないでください!」の、あまり目立たないサブクラスのようです)。

4

12 に答える 12

65

一般に、いいえ、これはアンチパターンではありません。finally ブロックのポイントは、例外がスローされたかどうかに関係なく、確実にクリーンアップされるようにすることです。例外処理の全体的なポイントは、処理できない場合は、比較的クリーンなアウトオブバンド シグナリングの例外処理が提供することで、処理できる人にバブルアップさせることです。例外がスローされた場合にクリーンアップされることを確認する必要があるが、現在のスコープで例外を適切に処理できない場合、これはまさに正しいことです。finally ブロックがスローされないように、もう少し注意する必要があるかもしれません。

于 2009-03-02T03:14:56.650 に答える
30

ここでの本当の「アンチパターン」は、ブロック内で投げることができる何かをするfinallyことであり、キャッチを持たないことだと思います。

于 2009-03-02T03:19:51.100 に答える
17

全くない。

間違っているのは、finally 内のコードです。

finally は常に実行され、例外をスローする可能性のあるものをそこに配置するのは危険です (先ほど見たように)。

于 2009-03-02T04:15:15.883 に答える
11

最終的にキャッチせずに試してみても、まったく問題はありません。次の点を考慮してください。

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

例外がスローされ、このコードがそれを処理する方法がわからない場合、例外はコール スタックを呼び出し元にバブルアップする必要があります。この場合でも、ストリームをクリーンアップしたいので、キャッチなしで try ブロックを使用することは完全に理にかなっていると思います。

于 2009-03-02T03:15:03.187 に答える
5

これはアンチパターンではなく、メソッドの実行中に取得したリソースの割り当てを解除することが重要な場合に頻繁に行うことだと思います。

(書き込み用に) ファイル ハンドルを処理するときに行うことの 1 つは、ストリームを閉じる前に IOUtils.closeQuietly メソッドを使用してストリームをフラッシュすることです。これは例外をスローしません。


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

私は次の理由から、そのようにするのが好きです。

  • ファイルを閉じるときに例外を無視するのは完全に安全ではありません。ファイルにまだ書き込まれていないバイトがある場合、ファイルは呼び出し元が期待する状態にない可能性があります。
  • したがって、flush() メソッドの実行中に例外が発生した場合、それは呼び出し元に伝播されますが、それでもすべてのファイルが閉じていることを確認します。メソッド IOUtils.closeQuietly(...) は、対応する try ... catch ... ignore me ブロックよりも冗長ではありません。
  • 複数の出力ストリームを使用する場合、flush() メソッドの順序が重要です。他のストリームをコンストラクターとして渡すことによって作成されたストリームは、最初にフラッシュする必要があります。同じことが close() メソッドにも当てはまりますが、私の意見では、flush() の方がより明確です。
于 2009-03-02T05:23:03.193 に答える
1

次の形式で try/finally を使用します。

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

私はこれを try/catch/finally よりも好みます (+ 入れ子になった try/catch を finally に)。より簡潔だと思います。catch(Exception) は複製しません。

于 2009-03-02T03:40:19.367 に答える
1
try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

それもしないでください...もっと多くのバグを隠しただけです(正確に隠したわけではありません...しかし、それらに対処するのが難しくなりました。例外をキャッチすると、あらゆる種類のRuntimeException(NullPointerやArrayIndexOutOfBoundsなど)もキャッチされます) .

一般に、キャッチする必要のある例外 (チェック済み例外) をキャッチし、テスト時に他の例外を処理します。RuntimeExceptions は、プログラマー エラーに使用するように設計されています。プログラマー エラーは、適切にデバッグされたプログラムでは発生してはならないものです。

于 2009-03-02T03:40:36.573 に答える
1

私の意見では、何らかの問題を示すfinally場合が多いです。catchリソースの慣用句は非常に単純です。

acquire
try {
    use
} finally {
    release
}

Java では、ほとんどどこからでも例外を発生させることができます。多くの場合、取得はチェック済みの例外をスローします。これを処理する賢明な方法は、how lot の周りにキャッチを配置することです。恐ろしいヌルチェックを試さないでください。

あなたが本当にアナルになるつもりなら、例外の間に暗黙の優先順位があることに注意する必要があります. たとえば、ThreadDeath は、取得/使用/解放に関係なく、すべてを破壊する必要があります。これらの優先順位を正しく処理するのは見苦しいです。

したがって、Execute Around イディオムを使用してリソース処理を抽象化してください。

于 2009-03-02T12:49:12.307 に答える
1

catch ブロックのない try ブロックはアンチパターンだと思います。「キャッチなしで最後にしないでください」と言うのは、「キャッチなしで試してはいけない」のサブセットです。

于 2009-03-02T03:14:10.333 に答える
0

Try / Finalは、リソースを適切に解放する方法です。finallyブロックのコードは、tryブロックに入る前に取得されたリソースまたは状態にのみ作用するため、決してスローしないでください。

余談ですが、log4Jはほとんどアンチパターンだと思います。

実行中のプログラムを検査したい場合は、適切な検査ツールを使用してください(つまり、デバッガー、IDE、または極端な意味ではバイトコードウィーバーですが、ログステートメントを数行ごとに配置しないでください!)。

2つの例では、最初の例が正しいように見えます。2つ目は、ロガーコードを含み、バグを導入します。2番目の例では、最初の2つのステートメントによって例外がスローされた場合に例外を抑制します(つまり、例外をキャッチしてログに記録しますが、再スローはしません。これは、log4jの使用法で非常に一般的であり、アプリケーション設計の実際の問題です。変更を加えると、基本的に例外がないかのように先に進むため、システムが処理するのが非常に難しい方法でプログラムが失敗します(エラー時のVBベーシックのようなソートは次の構成を再開します)。

于 2009-10-12T04:44:13.780 に答える
-1

キャッチなしで試すことはアンチパターンだと思います。try/catch を使用して例外条件 (ファイル IO エラー、ソケット タイムアウトなど) を処理することは、アンチパターンではありません。

クリーンアップに try/finally を使用している場合は、代わりに using ブロックを検討してください。

于 2009-03-02T03:51:24.957 に答える