Throwable
をキャッチするのは悪い習慣ですか?
たとえば、次のようなものです:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
これは悪い習慣ですか、それとも可能な限り具体的にすべきですか?
これは悪い考えです。実際、Exception
をキャッチすることさえ、通常は悪い考えです。例を考えてみましょう:
_try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
_
ここで、getUserInput()がしばらくブロックし、別のスレッドが最悪の方法でスレッドを停止するとします(thread.stop()を呼び出します)。 catchブロックはThreadDeath
エラーをキャッチします。これは非常に悪いです。その例外をキャッチした後のコードの動作はほとんど定義されていません。
同様の問題が、例外のキャッチで発生します。多分getUserInput()
は、InterruptException、結果のログ記録中に許可が拒否された例外、または他のあらゆる種類の失敗のために失敗しました。何がうまくいかなかったのか、それが原因で、問題を修正する方法もわからないので、あなたにはわかりません。
次の3つのオプションがあります。
1-処理方法を知っている例外を正確にキャッチします。
_try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
inputNumber = 10; //Default, user did not enter valid number
}
_
2-発生した例外を再スローし、処理方法がわからない:
_try {
doSomethingMysterious();
} catch(Exception e) {
log.error("Oh man, something bad and mysterious happened",e);
throw e;
}
_
3-再スローすることを覚えておく必要がないように、finallyブロックを使用します。
_ Resources r = null;
try {
r = allocateSomeResources();
doSomething(r);
} finally {
if(r!=null) cleanUpResources(r);
}
_
また、Throwable
をキャッチすると、特別な処理が必要なInterruptedException
もキャッチできることに注意してください。詳細については、 InterruptedExceptionによる処理 を参照してください。
未チェックの例外のみをキャッチしたい場合は、このパターンも考慮する必要があります
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
このように、コードを変更し、チェック済み例外をスローできるメソッド呼び出しを追加すると、コンパイラーはそのことを通知し、この場合の対処方法を決定できます。
メソッドから例外バブルを絶対に出せない場合は、悪い習慣ではありません。
本当に例外を処理できない場合は、悪い習慣です。メソッドシグネチャに「スロー」を追加する方が、キャッチして再スローするだけでなく、さらに悪いことに、RuntimeExceptionでラップして再スローする方がよいでしょう。
errorクラスのjavadocから直接(これらをキャッチしないことをお勧めします):
* An <code>Error</code> is a subclass of <code>Throwable</code>
* that indicates serious problems that a reasonable application
* should not try to catch. Most such errors are abnormal conditions.
* The <code>ThreadDeath</code> error, though a "normal" condition,
* is also a subclass of <code>Error</code> because most applications
* should not try to catch it.
* A method is not required to declare in its <code>throws</code>
* clause any subclasses of <code>Error</code> that might be thrown
* during the execution of the method but not caught, since these
* errors are abnormal conditions that should never occur.
*
* @author Frank Yellin
* @version %I%, %G%
* @see Java.lang.ThreadDeath
* @since JDK1.0
Throwableのキャッチは、エラーを熱狂的にスローするライブラリを使用している場合に必要になることがあります。そうしないと、ライブラリがアプリケーションを強制終了する場合があります。
ただし、これらの状況では、すべてのThrowableではなく、ライブラリによってスローされる特定のエラーのみを指定するのが最適です。
Throwableは、例外だけでなく、スロー可能なすべてのクラスの基本クラスです。 OutOfMemoryErrorまたはKernelErrorをキャッチした場合にできることはほとんどありません( When to catch Java.lang.Error? を参照)
例外をキャッチするだけで十分です。
それはあなたのロジックに依存するか、あなたのオプション/可能性にもっと特定することです。意味のある方法で対応できる特定の例外がある場合は、最初にそれをキャッチして実行できます。
存在せず、すべての例外とエラー(たとえば、エラーメッセージで終了する)に対して同じことを行うと確信している場合、スロー可能オブジェクトをキャッチすることは問題ではありません。
通常、最初のケースが保持され、スロー可能オブジェクトをキャッチしません。しかし、それを捕まえることがうまくいく場合はまだたくさんあります。
非常に悪い習慣として説明されていますが、rareの場合に役立つだけでなく必須である場合があります。以下に2つの例を示します。
ユーザーに意味のある完全なエラーページを表示する必要があるWebアプリケーション。このコードは、すべてのリクエストハンドラー(サーブレット、Strutsアクション、またはコントローラー....)を囲む大きなtry/catch
であるため、これが発生することを確認します。
try{
//run the code which handles user request.
}catch(Throwable ex){
LOG.error("Exception was thrown: {}", ex);
//redirect request to a error page.
}
}
別の例として、口座振替ビジネスに役立つサービスクラスがあるとします。このメソッドは、転送が完了した場合はTransferReceipt
を返し、失敗した場合はNULL
を返します。
String FoundtransferService.doTransfer( fundtransferVO);
イメージングを行うと、ユーザーからList
の資金振替が行われ、上記のサービスを使用してすべてを行う必要があります。
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}
しかし、any例外が発生するとどうなりますか? 1つの転送が成功した場合とそうでない場合があるため、停止しないでください。すべてのユーザーList
を続けて、各転送に結果を表示する必要があります。したがって、このコードになります。
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}catch(Throwable ex){
LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
}
}
多数のオープンソースプロジェクトを参照して、throwable
が実際にキャッシュおよび処理されていることを確認できます。たとえば、次はTomcat
、struts2
およびprimefaces
の検索です。
https://github.com/Apache/Tomcat/search?utf8=%E2%9C%93&q=catch%28Throwablehttps://github.com/Apache/struts/search ?utf8 =%E2%9C%93&q = catch%28Throwablehttps://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable
質問は少しあいまいです。 「Throwable
をキャッチしても大丈夫ですか」、または「Throwable
をキャッチしても何もしないでよろしいですか」と尋ねていますか?ここでは多くの人が後者に答えましたが、それは副次的な問題です。 Throwable
またはIOException
をキャッチしているかどうかに関係なく、例外を「消費」または破棄しないでください。
例外を伝播する場合、回答(非常に多くの質問に対する回答など)は「依存します」です。それは、例外をどうするかによって異なります。なぜそれをキャッチしているのでしょうか。
Throwable
をキャッチする理由の良い例は、エラーがある場合に何らかのクリーンアップを提供することです。たとえば、JDBCでは、トランザクション中にエラーが発生した場合、トランザクションをロールバックする必要があります。
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
例外は破棄されず、伝播されることに注意してください。
しかし、一般的なポリシーとして、Throwable
をキャッチするのは、理由がなく、どの特定の例外がスローされているかを見るのが面倒だからです。
throwableを使用する場合、Errorもカバーします。
例
public class ExceptionTest {
/**
* @param args
*/
public static void m1() {
int i = 10;
int j = 0;
try {
int k = i / j;
System.out.println(k);
} catch (Throwable th) {
th.printStackTrace();
}
}
public static void main(String[] args) {
m1();
}
}
出力:
Java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.Java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.Java:25)
Throwableをキャッチすることは一般に悪い習慣ですが(この質問に対する多数の回答で説明されています)、Throwable
をキャッチすることが有用なシナリオは非常に一般的です。私の仕事で使用するこのようなケースの1つを、簡単な例で説明しましょう。
2つの数字の加算を実行する方法を考えてください。加算が成功すると、特定の人に電子メールアラートが送信されます。返された番号は重要であり、呼び出し元のメソッドで使用されていると仮定します。
public Integer addNumbers(Integer a, Integer b) {
Integer c = a + b; //This will throw a NullPointerException if either
//a or b are set to a null value by the
//calling method
successfulAdditionAlert(c);
return c;
}
private void successfulAdditionAlert(Integer c) {
try {
//Code here to read configurations and send email alerts.
} catch (Throwable e) {
//Code to log any exception that occurs during email dispatch
}
}
電子メールアラートを送信するコードは多くのシステム構成を読み取るため、そのコードブロックからさまざまな例外がスローされる可能性があります。ただし、アラートディスパッチ中に発生した例外が呼び出し元のメソッドに伝播することは望ましくありません。そのメソッドは、呼び出し元のメソッドが提供する2つの整数値の合計に関係しているためです。したがって、電子メールアラートをディスパッチするコードは、try-catch
ブロック。Throwable
がキャッチされ、例外が記録されるだけで、残りのフローを続行できます。
一般的に言って、Error
sをキャッチすることは避けたいのですが、そうすることが適切な(少なくとも)2つの特定のケースを考えることができます。
AssertionError
に応答してアプリケーションをシャットダウンしたい場合。Throwableは、すべてのエラーと例外のスーパークラスです。 catch節でThrowableを使用すると、すべての例外をキャッチするだけでなく、すべてのエラーもキャッチします。エラーは、アプリケーションによって処理されることを意図していない重大な問題を示すためにJVMによってスローされます。その典型的な例は、OutOfMemoryErrorまたはStackOverflowErrorです。どちらも、アプリケーションの制御外にあり、処理できない状況が原因です。したがって、Throwableの例外はThrowable内にのみ存在すると確信している場合を除き、Throwableをキャッチしないでください。