例外
例外処理
たとえコンパイルできたとしても、ユーザーの思いがけない操作などで
プログラムの実行中にプログラムが実行不可能になるような事象が起こります
このような現象を例外と呼びます
例外は、存在しないファイルを開こうとしたり、0で除算した時に起こります
例外は0の除算で学習するのが一般的なので、この場でも0の除算による例外から覚えましょう
次のプログラムは、メソッド内で0を除していします
コンパイルは問題なくできるので、次のようなプログラムを作成して実行してください
class test {
public static void main(String args[]) {
System.out.println(10 / 0);
}
}
これを実行すると、Javaのヴァージョンで幾分違いますが、次のような文字が出力されます
Exception in thread "main" java.lang.ArithmeticException: / by zero
at test.main(test.java:3)
0で除算した時に例外オブジェクトが生成されています
java.lang.ArithmeticExceptionというのが、そのクラスです
Javaに限らず、このような例外が発生するとプログラムは「割り込み」をします
先ほどの場合は例外オブジェクトがプログラムに割り込んで、例外処理をして実行を停止させています
例外処理は、このように例外が発生した時に割り込んで処理するプログラムです
ユーザーが処理中に例外が発生して、意味不明な文字を残して終わるようではかっこいいプログラムとは言えません
そこで、例外発生時に割り込むルーチンを定義することで、より上品に終わらせることができます
例外ルーチンを呼び出すには、例外が発生しているかどうかを監視しなければいけません
例外の監視はtryステートメントで囲まれたブロックで行われます
try {
statements...;
}
tryブロックないのステートメントやメソッドで例外が発生すると
プログラムはその例外の処理ができる処理班catchブロックを検索します
catchブロックはtryブロックの後方に記述し、例外に備えます
catch (ErrType1 param) {
statements...;
}
catch (ErrTypeN param) {
statements...;
}
catchは目的の例外処理に応じて任意の数だけ書けます
ErrTypeには例外オブジェクトを指定します
paramには例外のパラメータが入っています
例外オブジェクトの種類がパラメータと一致した時そのcatchブロックが実行されます
0の除算の時に発生した例外オブジェクトはjava.lang.ArithmeticExceptionでした
つまり、この例外オブジェクトをcatchに指定すれば、例外処理ができるようになります
class test {
public static void main(String args[]) {
try {
System.out.println(10 / 0);
}
catch (ArithmeticException err) {
System.out.println("エラー :" + err);
}
}
}
これを実行すると
エラー :java.lang.ArithmeticException: / by zero
となりました。こちらの指定どうりに例外処理されましたね
これはtry内であれば呼び出されたメソッド内で例外が起きても処理されます
さらに例外処理はcatchブロックの終了、またはtryが終了した時に
制御がfinallyブロックへ移行して実行されるという特徴があります
finallyは省略可能ですが、リソースの解放などに使われます
finally {
statements...;
}
tryブロックの後には、からなずcatchブロックかfinallyブロックのいずれかが存在しなければいけません
存在しなかった場合にはコンパイルエラーが発生します
class test {
public static void main(String args[]) {
try {
System.out.println(10 / 0);
}
catch (ArithmeticException err) {
System.out.println("エラー :" + err);
}
finally {
System.out.println("Kitty on your lap");
}
}
}
こうすれば、catchブロックの処理が終了するとfinallyに制御が移ります
確認できれば、例外が起こらないようにtryブロックを変更し
通常にtryブロックの処理が終わるようにして実行して見るのも良いでしょう
tryブロックの終了時にもfinallyが実行されるのがわかるはずです
注意点ですがcatchの例外オブジェクトに指定できるのはjava.lang.Throwableオブジェクトです
java.lang.ArithmeticExceptionもjava.lang.Throwableのサブクラスです
あらゆる例外オブジェクトのスーパークラスがjava.lang.Throwableなので、必ずこれの拡張クラスを指定します
catchの順番
先ほどは、簡単に例外処理(例外ハンドラ)について学習しました
今回はより詳しく catch ブロックの検索順について見てみましょう
例外が発生すると、そのtryブロックの直後のcatchブロックから例外ハンドラの検索が始まります
しかしtryブロック内でメソッドを呼び出し、さらにそのメソッド内にtryブロックがあれば…その関係は複雑ですね
このようなケースを考えて、catchの検索メカニズムを熟知する必要があります
メソッドのもっとも深い階層で例外が発生した場合、まずは自分のメソッド内のcatchを検索します
それでもcatchが存在しない場合はfinallyを実行して呼び出しもとのcatchを検索します
自分の(相対)メソッドのtryを終了した時にfinallyを実行するということを忘れないでください
こうして、main()メソッドまでスタックをさかのぼって検索し
それでも検出されなければ既定の例外ハンドラが呼び出されます
class test {
public static void main(String args[]) {
try {
first();
}
catch (ArithmeticException err) {
System.out.println("エラー1 :" + err);
}
finally {
System.out.println("無駄無駄無駄無駄ァ");
}
}
static void first() {
try {
second();
}
catch (ArrayIndexOutOfBoundsException err) {
System.out.println("エラー2 :" + err);
}
finally {
System.out.println("ウィリィィィィィィィィ");
}
}
static void second() {
try {
System.out.println(10 / 0);
}
catch (ClassCastException err) {
System.out.println("エラー3 :" + err);
}
finally {
System.out.println("裁くのは俺のスレッドだぁ");
}
}
}
もっとも深い階層のsecond()メソッド内で例外が発生します
しかし、second()内のtryブロックに該当するcatch()はありません
ClassCastException は、不正なキャストが行われた時の例外オブジェクト
ArrayIndexOutOfBoundsExceptionは、存在しない配列の要素を参照すると発生する例外です
そこで、second()のtryブロックをfinallyを実行して抜け出し
first()を探し、それでもないのでfinallyを実行してmain()メソッドまでさかのぼります
main()メソッドのtryブロックに、該当する例外オブジェクトがあるので、これを実行して
さらにfinallyを実行して終了します
裁くのは俺のスレッドだぁ
ウィリィィィィィィィィ
エラー1 :java.lang.ArithmeticException: / by zero
無駄無駄無駄無駄ァ
このような結果が得られました。それぞれのtryブロックを終了する時にfinallyを実行しています
try
例外が発生する可能性のあるブロックを指定します
tryブロックには対応するcatchまたはfinallyいずれかがひとつ以上必要です
catchで処理できる例外が検索されなかった場合、スレッドをさかのぼって制御が移行します
catch
例外処理ブロックを定義します
例外の型と受け取るパラメータの引数名を指定し
パラメータが型を一致すれば、定義された例外処理を開始します
finally
tryまたはcatchブロックの後に続くfinallyブロックの定義です
finallyは省略可能で、tryブロックが最後まで実行された時に必ず実行します
また、例外が発生しcatchが実行された時、catchの終了時にもfinallyが実行されます
通常はクリーンアップ処理をfinallyで行います