プログラミング応用b 第6回『例外』 |
【例外処理の基礎】
例外(exception)とは,文字通り,「例外的な出来事」,すなわちエラーのことである。Javaの持つ例外
処理メカニズムでは,処理の実行中にエラーが発生した場合,エラーの内容を表す例外オブジェクトと呼ば
れるオブジェクトを,エラー通知に使う。
この例外オブジェクトを使ってエラーの通知をすることを,“例外を投げる(スロー,throw)”と言う。
例外オブジェクトは,エラーを伝える通信筒と思えばよい。
●例外オブジェクトの型
例外オブジェクトは,java.langパッケージに用意された,特別なクラスThrowableのサブクラスのオブジェ
クトである。下図Fig.2に,例外オブジェクトのクラスの継承関係を示す。Throwableは,すべての例外クラ
スのスーパークラスである。
Throwableには,ErrorとExceptionの2つのサブクラスが定義されている。また,Exceptionには,RuntimeException
というサブクラスが用意されている。ErrorとRuntimeExceptionは,Java仮想マシンが通知してくる特別な
例外を表している。
つまり,一般のプログラマは,Java APIで用意されているExceptionのサブクラスを使うか,Exceptionのサブクラスを
自分で定義するかして使うようにする。
●例外を投げるthrow文
実際に例外を通知する(=例外を投げる)には,throw文(下図Fig.3-(1))を使用する。たとえば,Exception
のサブクラスMyExceptionを定義してあるとして,このMyException型の例外オブジェクトを“投げる”には,
throw new MyException( );
などとする。
例外が投げられると,処理はそこで中断される。そして,エラーを処理できるところが,その例外オブジェクト
を受け取って(キャッチ,catch),処理を再開し,エラー処理を行う。イメージ的には,エラーが起こった部分で
エラーの内容を記したボール(例外オブジェクト)を,「誰かエラー処理してね」と言って,投げるような様子を想
像するとよいだろう。
●tryブロック
例外処理の構文を,Fig.3-(2)に示す。形式的には,予約語のついたブロックがつらなった形になっている。
最初のブロックがtryブロックである(Fig.3-(2)①)。このtryブロックの中に通常の処理を書いておく。つまり,
この部分は通常の処理を“試す(try)”部分なのである。この通常の処理の中でエラーが起きた場合,例外を
“投げる”ことになる。
●catchブロック
tryブロックに続くのが,catchブロックである(Fig.3-(2)②)。tryブロックで投げられた例外オブジェクト
は,このcatchブロックで“受け取る”ことができる。catchブロックは,返り値なしのメソッドのようなもので,
catch( MyException e ) {}
というように,例外オブジェクトを受け取るための仮引数を1個だけ持つことができる(ここで,MyExceptionは,
Exceptionのサブクラスとする)。そして,受け取った例外オブジェクトを参照するなどして,エラー処理を行う。
catchブロックは,0個以上,繰り返して複数書くことが可能である。たとえば,
try { throw new MyException( ); /* MyException型オブジェクトを投げる */ }
catch( MyException me ) {}
catch( Exception e ) {}
という具合である。
このように,複数のcatchブロックがある場合,最初のcatchブロックから順序よく見ていって,投げられた
例外オブジェクトの型と一番最初に一致したcatchブロックが選択される。上の例では,1番目と2番目のcatch
ブロックの仮引数型は,それぞれ,MyException,Exceptionとなっている。
投げられた例外オブジェクトは,MyException型なので,型だけ見れば,MyExceptionもExceptionも一致す
る(MyExceptionが,Exceptionのサブクラスであるため)。しかし,1番目のcatchブロックの方が先に一致する
ので,実際には,1番目のcatchブロックが選択されることになる。そのため,1番目のcatchブロックの仮引数
meがtryブロックで投げられた例外オブジェクトを参照するようになり,1番目のcatchブロックの冒頭から処理
が再開される。
ちょっと注意しなければならないのは,
try { } catch( Exception e ) { } catch( MyException me ) { } // このcatchブロックは絶対に実行されない
try{ (1)前処理 (2)中心的な作業 } catch( 例外型 仮引数 ) { エラー処理 } finally { (3)後処理 }
●例外発生時の処理の流れ
基本的な例外処理の流れを,下図Fig.3-(3),(4)の例でおさらいしてみよう。ここで,MyExceptionとYourException
は,Exceptionクラスの直接のサブクラスであるとする(Fig.3-(3)①)。
最初にFig.3-(3)の例を見てみよう。まず,tryブロックの中の処理1を実行し,次にMyException型の例外オブジェクト
を投げる。すると,処理はそこで中断される。つまり,処理2は実行されない(Fig.3-(3)②)。
次に,MyException型の例外オブジェクトをキャッチできるcatchブロックが探されることになる。最初に見つかるのは,
2番目のcatchブロックである。そのため,2番目のcatchブロックの仮引数meが投げられた例外オブジェクトを参照するよ
うに設定し,2番目のcatchブロックの先頭から処理が再開される(Fig.3-(3)③)。
(2番目の)catchブロックの処理が終了すると,finallyブロックがあるので,finallyブロックに処理が移り,後処理を実行
する(Fig.3-(3)④)。
finallyブロックの処理を終えると,try-catch-finallyブロックの次の文からまた実行されていく(Fig.3-(3)⑤)。
次に,Fig.3-(4)の例を見てみよう。この例では,tryブロック内で例外が投げられず,処理1から処理4まですべて実行すること
になる(Fig.3-(4)①)。例外が投げられなかったので,tryブロック終了後,すぐにfinallyブロックに処理が移る(Fig.3-(4)②)。
そして,finallyブロックの処理が実行された後,try-catch-finallyブロックの次の文からまた実行されていく(Fig.3-(4)③)。