Javaは「例外」という方法を使って、様々な処理の分岐を行います。
┏━━━━━┓ ┃処理の流れ┃ ┗━━┯━━┛ │ ┏━━┷━━┓ ┏━━━━┓ ┃例外の発生┠─→┃例外処理┃ ┗━━┯━━┛ ┗━━━━┛ × ↓ ┏━━━━━┓ ┃通常の流れ┃ ┗━━━━━┛
Javaでは、処理の途中で「Exception」クラスから派生した例外クラスを初期化して、「throw」命令に引き渡すことで例外を発生させます。
例外が発生した場合は、これから説明する2種類の方法のいずれかで、例外を処理をしなければなりません。
┏━━━━━━━┓ ┏━━━━━━━┓ ┃Exceptionから ┠─例外発生─→┃2種類の方法の ┃ ┃派生したクラス┃ ┃いずれかで ┃ ┗━━━━━━━┛ ┃例外を処理 ┃ ┗━━━━━━━┛
throw new Exception(); // 強制的にエラーを発生
このExceptionは、実際にはExceptionから派生した例外クラスになります。Javaではこの例外クラスの種類によって、どんな例外が発生したかを判断して処理を行います。
┏━━━━━━┓ ┏━━━━━━━━━━━━━━┓ ┃適切な例外 ┠─例外発生─→┃投げられた例外クラスの種類で┃ ┃クラスを選ぶ┃ ┃どんな例外が起きたか分かる ┃ ┗━━━━━━┛ ┗━━━━━━━━━━━━━━┛
// ファイル読み込み時の例外 throw new IOException(); // データ形式の例外(メッセージ付き) throw new DataFormatException("テキスト形式でない");
上記のIOExceptionも、DataFormatExceptionも、Exceptionから派生した例外クラスです。こういった例外がthrowされる場合は、2種類の方法のいずれかで例外を処理をしなければなりません。そうしなければコンパイルエラーになります。
ただし、例外処理が必ずしも必須でないケースもあります。それは、RuntimeExceptionから派生した例外クラスで例外を発生させた場合です。この場合は、例外を処理するプログラムを書かなくても、コンパイルエラーになりません。
メソッドでは、メソッドの引数の後に「throws」と書き、その後に例外クラスを書くことで、そのメソッドで発生した例外をメソッド内で処理せず、呼び出し元に投げることができます。
この際、「,(カンマ)」で区切ることで、複数の例外を書くことができます。
void method() throws ExceptionA, ExceptionB { 処理1 throw new ExceptionA(); 処理2 throw new ExceptionB(); }
例外が発生した場合、例外が発生した場所でメソッドの処理は打ち切られ、呼び出し元に処理が戻ります。そして、復帰した場所で、同じ例外が発生します。
┏Method1 ━━━━━┓ ┃┌─────┐ ┃ ┏Method2 ━━━━━━┓ ┃│Method2() ───────呼び出し──→┃────┐ ┃ ┃│←──────┐┃ ┃ ↓ ┃ ┃└─────┘ └──処理を中断して────処理中に例外発生 ┃ ┃ここで例外発生 ┃ 例外を投げて戻る ┃ ┃ ┃と見なす! ┃ ┗━━━━━━━━━━┛ ┗━━━━━━━━━┛
この例外は、例外を処理する2種類の方法のいずれかで処理しなければなりません。ここで説明したthrowsを使う例外処理で、さらに上位の呼び出し元メソッドに例外処理を任せることもできます。
┏Method0 ━━━┓ ┏Method1 ━━━┓ ┏Method2 ━┓ ┃┌─────┐┃ ┃┌─────┐┃ ┃ ┃ ┃│Method1() ──呼び出し→┃│Method2() ──呼び出し→┃例外発生!┃ ┃│←───────丸投げ──│←───────丸投げ────┘ ┃ ┃└─────┘┃ ┃└─────┘┃ ┃ ┃ ┃例外発生! ┃ ┃例外発生! ┃ ┃ ┃ ┗━━━━━━━┛ ┗━━━━━━━┛ ┗━━━━━┛