目次 | 前 | 次 | 索引 | Java言語規定 第2版 |
プログラムが,Javaプログラム言語の意味制約に違反するとき,Java仮想計算機は,このエラーを例外(exception)としてプログラムに知らせる。それらの違反の例として,配列境界の外でのインデクス付けがある。あるプログラム言語及びその処理系は,プログラムを強制的に終了することによって,それらのエラーに対応する。別のプログラム言語は処理系に任意又は予測不能な方法で対応することを許している。これらの方法のいずれも,Javaプラットフォームの設計目標,移植性及び頑健性とは適合しない。代わりに,Javaプログラム言語は,意味制約に違反したとき,例外を投げ,例外が発生した点からプログラマが指定可能な点への非局所的な制御の移動を引き起こすことを規定している。例外は,それが発生した点から投げられる(thrown)と言い,制御が移動する点で捕捉される(caught)と言う。
プログラムは,throw
文(14.17)を使用して,明示的に例外を投げることもできる。
throw
文の明白な使用は,普通なら負の値を期待しないところで整数値-1
のような予期しない値を返し,エラー条件を処理する古風な方式の代替手段を提供する。経験的に,その予期しない値は,呼出し元で無視されるか又は検査されないことが多いことが知られており,頑健的でない若しくは望ましくない振る舞い又はその両方をプログラムにもたらす。
すべての例外は,クラスThrowable
のインスタンス又はその下位クラスの一つによって表現される。それらのオブジェクトは,例外が発生する点からそれを捕捉するハンドラへと情報を運ぶために使用される。ハンドラは,try
文(14.19)のcatch
節によって構築される。例外を投げる過程において,Java仮想計算機は,現在のスレッド内で開始はしたが完了していない,任意の式,文,メソッド及びコンストラクタ呼出し,静的初期化子並びにフィールド初期化式を,一つずつ中途完了する。この処理は,その例外のクラス又はその例外のクラスの上位クラスの名前を指定することによってその特定の例外を扱うことを示すハンドラを見つけるまで続けられる。該当するハンドラが見つからない場合,メソッドuncaughtException
を現在のスレッドの親となるThreadGroup
に対して呼び出す。この方法によって,例外が処理されないままにしないためのあらゆる努力がなされる。
Javaプラットフォームの例外機構は,synchronized
文(14.18)及びsynchronized
メソッド(8.4.3.6, 15.12)の呼出しが中途完了したときに,ロックが解放されされるように,同期モデル(17)と統合されている。
例外の様々な原因(11.1)を11.では規定する。例外をコンパイル時(11.2)に検査する方法及び実行時(11.3)に処理する方法を詳しく述べる。詳細な例(11.4)を示し,その後,例外階層の説明(11.5)を行う。
Throwable
のインスタンス及びその下位クラスのインスタンスによって表現される。これらのクラスをまとめて,例外クラス(exception classes)とする。
throws
節は,結果となり得る各検査例外について,その例外クラス又はその例外クラスの上位クラスの一つを言及しておかなければならない。例外ハンドラの有無についての,このコンパイル時検査は,適切に扱われない例外の数を減らすように,設計されている。
非検査例外クラス(unchecked exceptions classes)は,クラスRuntimeException
及びその下位クラス,並びにクラスError
及びその下位クラスとする。他のすべての例外クラスは,検査例外クラス(checked exceptions classes)とする。Java APIは,検査及び非検査の幾つかの例外クラスを定義している。検査及び非検査の付加的例外クラスが,プログラマによって宣言されてもよい。Java API及びJava仮想計算機が定義している例外クラスの階層及び例外クラスの規定に関しては,11.5を参照のこと。
throws
節内で指定された検査例外は,メソッド又はコンストラクタの実装者及び利用者との間の契約の一部とする。上書きするメソッドのthrows
節は,上書きされるメソッドがそのthrows
節で投げることを許されていない検査例外を,このメソッドが投げることを指定してはならない。インタフェースが関係するときには,一つ以上のメソッド宣言が一つの上書き宣言によって上書きされてもよい。この場合,上書きする宣言は,すべて(all)の上書きされる宣言(9.4)と互換性のあるthrows
節をもたなければならない。
静的初期化子(8.7),クラス変数初期化子,及びインスタンス初期化子又はインスタンス変数初期化子及びインタフェース(8.3.2)は,検査例外を生じてはならない。もし生じる場合,コンパイル時エラーが発生する。 インスタンス初期化子又は無名クラス(15.9.5)内のインスタンス変数初期化子へ適用される制約事項はない。
Error
及びその下位クラス)である非検査例外クラスは,コンパイル時に検査されない。その理由は,それらのエラーは,プログラム内の多くの点で発生可能であり,かつ回復が困難又は不可能なためである。そのような例外を宣言しているプログラムは,乱雑で要領を得ないものとなる。
RuntimeException
及びその下位クラス)は,コンパイル時に検査されない。その理由は,そのような例外を宣言させることは,Javaプログラムの正当性の確立を大して援助しない,と設計者が判断したためである。 Javaプログラム言語の演算及び構文の多くは,実行時例外を生じることができる。 コンパイラが利用できる情報及びコンパイラが実行する分析のレベルは,通常,たとえプログラマには明白であるとしても,それらの実行時例外が起こり得ないと証明するためには十分でない。そのような例外クラスの宣言を要求することは,単にプログラマを苛立たせるだけだと考えられる。
例えば,あるコードが,その構造によって,決してnull
参照を含むことがない循環的なデータ構造を実装したとする。このとき,プログラマは,NullPointerException
は,決して起こらないと確信できる。しかし,コンパイラがそれを証明するのは,困難であろう。データ構造のそのような大域的な特性を確立するために必要な定理証明技術は,本規定の範囲を超える。
try
文(14.19)のcatch
節へ制御が移される。
文又は式は,その文若しくは式が,catch
節を一部とするtry
文のtry
ブロック内に出現する場合,又はその文若しくは式の呼出し元が,catch
節によって動的に囲まれている場合に,catch
節によって動的に囲まれる(dynamically enclosed)という。
文又は式の呼出し元(caller)は,それが発生した場所に依存する。
newInstance
メソッド呼出しとする。
static
変数のための初期化子内であれば,呼出し元は,その初期化を引き起こすように,そのクラス又はインタフェースを使用した式とする。
catch
節が例外を取り扱う(handles)かどうかは,投げられたオブジェクトのクラス及びcatch
節のパラメタの宣言された型とを比較することによって決定する。catch
節は,もしそのパラメタの型が,その例外のクラス又はその例外のクラスの上位クラスならば,その例外を取り扱う。すなわち,catch
節は,宣言されたパラメタ型のinstanceof
式(15.20.2)がtrueを返す任意の例外オブジェクトを捕捉する。
例外が投げられるときに発生する制御移動は,例外を扱えるcatch
節に出会うまで,式(15.6)及び文(14.1)の中途完了を引き起こす。その後,そのcatch
節のブロックを実行することで,処理を続行する。例外を引き起こしたコードは,決して再開されないものとする。
例外を取り扱うcatch
節を見い出せなければ,現在のスレッド(例外に遭遇したスレッド)を終了する,しかしその前に,すべてのfinally
節が実行され,さらに現在のスレッドの親であるThreadGroup
に対して,メソッドuncaughtException
が呼び出される。
たとえ他のコードのブロックが中途完了しても,一つのコードのブロックを確実に順次実行することが望ましい状況では,finally
節(14.19.2)を伴ったtry
文を使用するとよい。
try-finally
文又はtry-catch-finally
文内のtry
又はcatch
ブロックが中途完了した場合,たとえ対応するcatch
節が見つからなくても,例外の伝播中にfinally
節が実行される。
finally
節が,try
ブロックの中途完了のために実行され,しかもfinally
節自身も中途完了した場合,try
ブロックの中途完了の理由は破棄され,新しい中途完了の理由がそこから伝播される。
中途完了及び例外捕捉のための正確な規則は,各文の規定と共に14で,式に対しては,15(特に15.6)で詳細に規定する。
高品質な機械コードを生成するために,非同期例外のセマンティクスの正しい理解が必要である。
Thread
又はクラスThreadGroup
のメソッドstop
の呼出し
stop
メソッドは,一つのスレッドから,他のスレッド又は指定されたスレッドグループ内のすべてのスレッドに影響するように呼び出されてもよい。他のスレッド又はスレッドの任意の実行点で発生できるので,非同期的とする。
InternalError
は,非同期と見なされる。
Javaプラットフォームでは,(Java仮想計算機が)非同期例外を投げる前に,少量で限られた量の処理を(処理系が)実行することができる。この遅延は,最適化されたコードが,Javaプログラム言語の意味規則に従いながら,それらの例外を取り扱うのに有用な場所で,例外を検出し投げることができるようにするために許されている。
単純な実装では,各制御転送命令の位置で,非同期例外用のポーリングを行ってもよい。 プログラムの大きさは限られているので,非同期例外の検出の時間限界はプログラムの大きさで決定する。制御転送の間では,いかなる非同期的例外も発生しないので,コード生成器は,より高い性能のために実行する制御転送間での計算順序の並べ替えに多少の柔軟性をもつ。
参考
Mark Feeley による論文,Polling Efficiently on Stock
Hardware, Proc. 1993 Conference on Functional Programming and Computer
Architecture, Copenhagen, Denmark, pp.179-187, をもっと詳しい参考資料として推薦する。
すべての例外と同様に,非同期例外は正確とする(11.3.1)。
引数を,class TestException extends Exception { TestException() { super(); } TestException(String s) { super(s); } } class Test { public static void main(String[] args) { for (int i = 0; i < args.length; i++) { try { thrower(args[i]); System.out.println("Test \"" + args[i] + "\" didn't throw an exception"); } catch (Exception e) { System.out.println("Test \"" + args[i] + "\" threw a " + e.getClass() + "\n with message: " + e.getMessage()); } } } static int thrower(String s) throws TestException { try { if (s.equals("divide")) { int i = 0; return i/i; } if (s.equals("null")) { s = null; return s.length(); } if (s.equals("test")) throw new TestException("Test message"); return 0; } finally { System.out.println("[thrower(\"" + s + "\") done]"); } } }
として,プログラムを実行すれば,次を出力する。divide null not test
この例は,例外クラス[thrower("divide") done] Test "divide" threw a class java.lang.ArithmeticException with message: / by zero [thrower("null") done] Test "null" threw a class java.lang.NullPointerException with message: null [thrower("not") done] Test "not" didn't throw an exception [thrower("test") done] Test "test" threw a class TestException with message: Test message
TestException
を宣言する。クラスTest
のメソッドmain
は,4回メソッドthrower
を呼び出し,4回のうち3回例外を投げる。メソッドmain
内のtry
文は,thrower
が投げる各例外を捕捉する。thrower
の呼出しが正常完了するか中途完了するかにかかわらず,起ったことを記述するメッセージを印刷する。
メソッドthrower
の宣言は,検査例外クラス(11.2)TestException
のインスタンスを投げることができるので,throws
節をもたなければならない。throws
節を省略すると,コンパイル時エラーが発生する。
各呼出しに対して発生している出力 "[thrower(...) done]
"が示すとおり,finally
節は,例外が発生するかどうかにかかわらず,thrower
のすべての呼出しで実行されていることに注意すること。
Object
の直接的下位クラスであるThrowable
(11.5)をルートとするクラス階層となる。クラスException
及びクラスError
は,Throwable
の直接的下位クラスとする。クラスRuntimeException
は,Exception
の直接的下位クラスとする。
プログラムは,throw
文で既存の例外クラスを使用できる,又は,Throwable
若しくはその下位クラスのいずれかとして,付加的な例外クラスを適宜定義可能とする。例外ハンドラに対するJavaプラットフォームのコンパイル時検査の利点を得るために,大部分の新しい例外クラスは,検査例外クラスとして定義することを一般的とする,具体的にはRuntimeException
の下位クラスでないException
の下位クラスとして定義する。
クラスException
は,例外からの回復を希望する普通のプログラムのためのすべての例外の上位クラスとする。クラスRuntimeException
は,クラスException
の下位クラスとする。 RuntimeException
の下位クラスは,非検査例外クラスとする。RuntimeException
以外のException
の下位クラスは,すべて検査例外クラスとする。
クラスError
及びその下位クラスは,通常,普通のプログラムが回復を期待しない例外とする。例外階層の詳細についてはJava API規定を参照のこと。
クラスError
は,Throwableの下位クラスで,Exceptionとは異なるとする。プログラムは,回復が可能かもしれないすべての例外を捕捉するために,次の形式を使用できる。
この記述は,回復が通常は不可能なエラーを除き使用される。} catch (Exception e) {
LinkageError
の下位クラスのインスタンスであるオブジェクトを投げる。
VirtualMachineError
の下位クラスのインスタンスであるオブジェクトを投げる。これらのエラーを定義を記述しているThe Java Virtual Machine Specification, Second Editionを参照のこと。
目次 | 前 | 次 | 索引 | Java言語規定 第2版 |