web-dev-qa-db-ja.com

try / catchには何を入れますか?

質問に関するメモ:これは重複ではありません 効率的なtry/catchブロックの使用法? これの後に質問されました。もう1つは重複です

Try/catchを使用する最良の方法は何だったのかと思っていました。 tryブロックの内容を最小限に制限するのが良いですか、それともすべてをそれに入れるのですか?

例を挙げて説明します。

コード1:

_try {
  thisThrowsAnException();
}
catch (Exception e) {
  e.printStackTrace();
}
thisDoesnt();
_

コード2:

_try {
  thisThrowsAnException();
  thisDoesnt();
}
catch (Exception e) {
  e.printStackTrace();
}
_

thisThrowsAnException()...が...例外をスローする可能性があると仮定して、thisDoesnt()...

2つの例の違いを知っています。例外がキャッチされた場合、thisDoesnt()は最初のケースではなく2番目のケースで呼び出されます。私にとって問題ではないので、その例外がスローされるとアプリケーションが停止するはずなので、なぜ一方が他方よりも優れたバージョンを使用するのでしょうか。

25
SteeveDroz

私の意見では、例外をスローする部分に依存するすべてをブロックに入れる必要があります。したがって、2番目の例の場合:

_try {
  thisThrowsAnException();
  thisDoesnt();
}
catch (Exception e) {
  e.printStackTrace();
}
_

thisDoesnt();thisThrowsAnException()の正常な実行に依存している場合は、それを含める必要があります。

thisThrowsAnException()が失敗した場合に実行しても意味がありますか?

46
Michael

例外をキャッチすることで、例外からrecoveringの責任を負うことに注意することが重要です。 tryブロックのスコープは、リカバリがどのように実行されるかに依存する必要があります。たとえば、次の2つのコードスニペットを見てください。

スニペット1:

for (Object object : objects) {
    try {
        performTaskOnObject(object);
    } catch (Exception e) {
        log.error("Failed to perform task on object", e);
    }
}

スニペット2:

try {
    for (Object object : objects) {
        performTaskOnObject(object);
    }
} catch (Exception e) {
    log.error("Failed to perform task on objects", e);
}

tryブロックのスコープを変更するだけで、これらの2つのスニペットは2つの非常に異なる回復戦略を示します。スニペット1では、1つ以上の障害が発生した後でも、残りのObjectsに対してタスクを試行できます。スニペット2では、失敗後に残りのObjectsに対してタスクを試行することはできません。

リカバリ戦略は、tryブロックをスコープするためのガイドになるはずです。

17

Michaelはすでに述べました のように、コーディングスタイルやベストプラクティスの問題だけではなく、アルゴリズムを念頭に置く必要があります。

コード1の利点は、maintainablityです。 try/catchは、例外をスローする可能性がある1行のコードをラップonlyします。したがって、try/catchブロックが表示された場合、それはすぐに明確になり、which操作でブロックをスローできます。これは、さまざまなIO例外をスローする一連の操作がある場合に非常に役立ちます。ステートメントを削除したり、コードの一部を別のメソッドにリファクタリングしたりしても、例外ハンドラーが失われたり、(キャッチされたランタイム例外の)不要なtry/catchハンドラーが残されたりすることはありません。

コード2の利点は、readabiltyです。多くのtry/catchブロックでは、メソッドが乱雑になり、これらのすべてのtry/catchステートメントとハンドラの背後にある実際のアルゴリズムを確認することが非常に難しくなります。

私自身のコードでは、バージョン2を好みます。クリーンなコードにつながる可能性が高いバージョンです。それでも、リファクタリング中はもっと注意を払わなければなりません。


トリビア-あなたがすべきでないことがcatchブロックに入れるのはe.printStackTrace();ステートメント;)log4j(または類似の何か)を追加し、log.debug(e)を使用してcatchブロックを初期化するようにIDEのテンプレートを変更することをお勧めします

11
Andreas_D

私は最初のバージョンを好むので、人間の読者にとって、プログラムのどの部分が例外をスローし、どのように処理されるかが明らかになります。実際のプログラムでは、thisDoesnt()は数行のコードであり、運が良ければそのうちの1つでも例外がスローされる可能性がありますが、コードのバージョン2ではその事実が隠されているため、例外処理でthisThrowsAnException();の失敗を処理するためのものなので、間違っています。そして、これらの数行のthisDoesnt()は、例外ハンドラをthisThrowsAnException();から分離しているため、接続がわかりにくくなっています。

したがって、私のコードのバージョンはそのようになります

boolean success=false;
try {
  thisThrowsAnException();
  success=true;
}
catch (Exception e) {
  e.printStackTrace();
}

if (success) {
  thisDoesnt();
}

またはエラーの場合は早期終了します:

try {
  thisThrowsAnException();
}
catch (Exception e) {
  e.printStackTrace();
  return null;  // false, "", whatever; or rethrow the exception
}

thisDoesnt();
2
user281377

2つのコードは、例外の存在下で異なることをしています。どちらか一方の好みはありません。必要なことを行う方を使用してください。

0
AProgrammer

「アプリケーションを停止する」には、2つの方法があることに注意してください。

  1. 例外をスローし、必要なことを実行してからプログラムを終了する(または再試行するなどの)中央のエラーハンドラーまでバブルします。

  2. 「System.exit(1)」を呼び出します。これは、最も単純なプログラムを除いて、あなたが望むものであることはめったにありません。単純なエラーがWebサーバーをクラッシュさせる可能性があるWebアプリケーション。また、リソースが適切に解放されていないなど。

System.exit()を使用する代わりに、例外を使用してエラーを示し、エラーから回復する方法を学ぶことを強くお勧めします。 finally句は、リソースのクリーンアップにも非常に重要です。

あなたの質問では、thisDoesnt()が失敗したかどうかに関係なくthisThrowsAnException()を実行する必要がある場合は、コード1です。失敗しない場合にのみ実行する必要がある場合は、コード2です。

0
user1249