構造化例外処理
「構造化例外処理」の構文はtry-catch-finallyの三つのブロックで構成され、tryブロックに行いたい処理、catchブロックに処理によって発生する可能性のある例外の回復処理、そしてfinallyブロックに例外発生の有無にかかわらず実行したい処理を記述します。
try { 行いたい処理; } catch (例外クラスの型 例外クラスの変数) { 例外の回復処理; } fainally { 例外発生の有無にかかわらず実行したい処理; }
さらに例外が発生した場合、その発生したメソッド内でキャッチして例外を処理することも、メソッド名に続けてthrows宣言を行い、例外処理をメソッドの呼び出し元に移譲することも出来ます。この場合、そのメソッド内でのその例外に対する例外処理は不要です。
なお、例外処理の移譲によってメソッド呼び出しのおお元で例外を一元管理して処理することが出来ます。
戻り値の型 メソッド名(引数並び) <strong>throws 例外クラスの型</strong> { }
除算によりArithmeticException(算術例外)が発生しても自分では例外処理をせず、呼び出しもとに例外処理を移譲する
int Divide(int num, int denum) throws ArithmeticException { return (num / denum); }
try ブロック内には、複数の文を記述できます。もし例外が発生したらtryブロック内の残りの処理は全て読み飛ばされ、対応するcatchブロックに制御が移ります。
catchブロックの目的はプログラムを正常な状態に復帰させることで、キャッチできる例外はすべての例外のルートクラスであるThrowableクラス、例外処理が必須のExceptionクラスだけでなく、例外処理が必須ではないErrorクラスやRuntimeExceptionクラスもキャッチできます。catchブロックの処理が終了すると「不具合は対処された」として、finallyブロックの処理に移ります。
finallyブロックは必ず実行されるブロックであり、例外をthrows宣言している場合、例外が移譲される前にfinallyブロックが実行されます。またcatchブロックにreturn文がある場合でも、return処理の前にfinallyブロックが実行され、その後catchブロックに戻りreturnが実行されます。
try-catch-finallyの構文は、各ブロックの順序を変更することはできません。誤った順序で記述するとコンパイルエラーになります。tryブロックとfinallyブロックは1つずつしか記述できず、複数記述するとコンパイルエラーになります。一方、catchブロックは複数記述できます。
なお、catchブロックを複数記述する場合はcatchする例外の順序はサブクラスを先頭にする必要があります。これは例外クラスのインスタンスも、ほかのクラスと同様にポリモーフィズムが成り立ち、サブクラスの例外クラスのcatchはスーパークラスの例外処理がcatch可能であり、結果サブクラスの例外処理がスーパークラスの例外処理の後に記述されていると、サブクラスの例外処理は常に実行されない事になるからです。このような場合、コンパイラーは到達不可能なコードをあるとして、コンパイルエラーを発生します。
public class SubException extends Exception { }
Exceptionのcatch文を先に記述
public class CatchOrder { public static void main(String[] args) { try { throw new SubException(); } catch (Exception e) { } catch (SubException e) { } } }
SubExceptionの例外もExceptionのcatch文が取ってしまうのでSubExceptionのcatch文は到達不可能なコードとなりコンパイルエラーになります。
>javac CatchOrder.java CatchOrder.java:7: エラー: 例外SubExceptionはすでに捕捉されています } catch (SubException e) { ^ エラー1個 >
SubExceptionのcatchブロックはExceptionのcatchブロックより前に置きます。
複数のtry-catchがネストしている場合、スローされた例外を受け取るのは、その例外に対応したもっとも近いcatch ブロックです。ネスト階層ごとにfinallyブロックがある場合は、ネストの内側から順にすべてのfinallyブロックが実行されます。