Javaプログラムは,throw文(14.16)を使用して,明示的に例外を投げることもできる。
これは,普通なら負の値を期待しないところで整数値-1のような予期しない値を返し,エラー条件を処理する古風な方式の代替手段を提供する。
経験的に,その予期しない値は,呼び出し元で無視されるか又は検査されないことが多いことが知られており,頑健的でない若しくは望ましくない振舞い又はその両方をプログラムにもたらす。
すべての例外は,クラスThrowableのインスタンス又はそのサブクラスの一つによって表現される。
それらのオブジェクトは,例外が発生する点からそれを捕捉するハンドラへと情報を運ぶために使用される。
ハンドラは,try文(14.18)のcatch節によって構築される。
例外を投げる過程において,Java仮想計算機は,現在のスレッド内で開始はしたが完了していない,任意の式,文,メソッド及びコンストラクタ呼出し,静的初期化子並びにフィールド初期化式を,一つずつ中途完了する。
この処理は,その例外のクラス又はその例外のクラスのスーパクラスの名前を指定することによってその特定の例外を扱うことを示すハンドラを見つけるまで続けられる。
該当するハンドラが見つからない場合,メソッドuncaughtException(20.21.31)を現在のスレッドの親となるThreadGroupに対して呼び出す。
この方法によって,例外が処理されないままにしないためのあらゆる努力がなされる。
Javaの例外機構は,synchronized文(14.17)及びsynchronizedメソッド(8.4.3.5,15.11)の呼び出しが中途完了したときに,ロックが解放されされるように,Javaの同期モデル(17)と統合されている。
例外(11.1)の様々な原因を11.では規定する。 例外をコンパイル時(11.2)に検査する方法及び実行時(11.3)に処理する方法を詳しく述べる。 詳細な例(11.4)を示し,その後,例外階層及び標準例外クラス(11.5)を規定する。
throw文(14.16)を実行した。
Thread(20.20.16)のメソッドstopを呼び出した。
Throwableのインスタンス及びそのサブクラスのインスタンスによって表現される。
これらのクラスをまとめて,例外クラス(exception classes)とする。
throws節は,結果となり得る各検査例外について,その例外クラス又はその例外クラスのスーパクラスの一つを言及しておかなければならない。
例外ハンドラの有無についての,このコンパイル時検査は,適切に扱われない例外の数を減らすために,設計されている。
非検査例外クラス(unchecked exceptions classes)は,クラスRuntimeException及びそのサブクラス,並びにクラスError及びそのサブクラスとする。他のすべての例外クラスは,検査例外クラス(checked exceptions classes)とする。
標準のJava APIは,検査及び非検査のいくつかの例外クラスを定義している。
検査及び非検査の付加的例外クラスが,Javaプログラマによって宣言されてもよい。
標準のJava API及びJava仮想計算機が定義しているJava例外クラスの階層及び例外クラスの規定に関しては,11.5を参照のこと。
throws節内で指定された検査例外は,メソッド又はコンストラクタの実装者及び利用者との間の契約の一部とする。
上書きするメソッドのthrows節は,上書きされるメソッドがそのthrows節で投げることを許されていない検査例外を,このメソッドが投げることを指定してはならない。
インタフェースが関係するときには,一つ以上のメソッド宣言が一つの上書き宣言によって上書きされてもよい。
この場合,上書きする宣言は,すべて(all)の上書きされる宣言(9.4)と互換性のあるthrows節をもたなければならない。
フィールドに対する変数初期化子(8.3.2)及び静的初期化子(8.5)は,検査例外を生じてはならない。 もし生じる場合,コンパイル時エラーが発生する。
Error及びそのサブクラス)である非検査例外クラスは,コンパイル時に検査されない。
その理由は,それらのエラーは,プログラム内の多くの点で発生可能であり,かつ回復が困難又は不可能なためである。
そのような例外を宣言しているJavaプログラムは,乱雑で要領を得ないものとなる。
RuntimeException及びそのサブクラス)は,コンパイル時に検査されない。その理由は,そのような例外を宣言させることは,Javaプログラムの正当性の確立を大して援助しない,とJava設計者が判断したためである。
Java言語の演算及び構文の多くは,実行時例外を生じることができる。
Javaコンパイラが利用できる情報及びコンパイラが実行する分析のレベルは,通常,たとえJavaプログラマには明白であるとしても,それらの実行時例外が起こり得ないと証明するためには十分でない。
そのような例外クラスの宣言を要求することは,単にJavaプログラマを苛立たせるだけだと考えられる。
例えば,あるコードが,その構造によって,決して空参照を含むことがない循環的なデータ構造を実装したとする。このとき,プログラマは,NullPointerExceptionは,決して起こらないと確信できる。しかし,コンパイラがそれを証明するのは,困難であろう。データ構造のそのような大域的な特性を確立するために必要な定理証明技術は,このJava言語規定の範囲を超える。
try文(14.18)のcatch節へ制御が移される。
文又は式は,
その文若しくは式が,catch節を一部とするtry文のtryブロック内に出現する場合,又はその文若しくは式の呼出し元が,catch節によって動的に囲まれている場合に,catch節によって動的に囲まれる(dynamically enclosed)という。
文又は式の呼出し元(caller)は,それが発生した場所に依存する。
newInstanceメソッド呼出しとする。
static変数のための初期化子内であれば,呼出し元は,その初期化を引き起こすように,そのクラス又はインタフェースを使用した式とする。
catch節が例外を取り扱う(handles)かどうかは,投げられたオブジェクトのクラス及びcatch節のパラメタの宣言された型とを比較することによって決定する。catch節は,もしそのパラメタの型が,その例外のクラス又はその例外のクラスのスーパクラスならば,その例外を取り扱う。同様に,catch節は,宣言されたパラメタ型のinstanceof(15.19.2)である任意の例外オブジェクトを捕捉する。
例外が投げられるときに発生する制御移動は,例外を扱えるcatch節に出会うまで,式(15.5)及び文(14.1)の中途完了を引き起こす。その後,そのcatch節のブロックを実行することで,処理を続行する。例外を引き起こしたコードは,決して再開されないものとする。
例外を取り扱うcatch節を見い出せなければ,現在のスレッド(例外に遭遇したスレッド)を終了する,しかしその前に,すべてのfinally節が実行され,さらに現在のスレッドの親であるThreadGroupに対して,メソッドuncaughtException(20.21.31)が呼び出される。
たとえ他のコードのブロックが中途完了しても,一つのコードのブロックを確実に順次実行することが望ましい状況では,finally節(14.18.2)を伴ったtry文を使用することができる。
try-finally文又はtry-catch-finally文内のtry又はcatchブロックが中途完了した場合,たとえ対応するcatch節が見つからなくても,例外の伝播中にfinally節が実行される。
finally節が,tryブロックの中途完了のために実行され,しかもfinally節自身も中途完了した場合,tryブロックの中途完了の理由は破棄され,新しい中途完了の理由がそこから伝播される。
中途完了及び例外捕捉のための正確な規則は,各文の規定と共に14.で,式に対しては,15.(特に15.5)で詳細に規定する。
Javaでの非同期例外は少なく,次の結果としてだけ発生する。
Thread(20.20.15,20.20.16)又はクラスThreadGroup(20.21.8,20.21.9)のメソッドstopの呼出し
InternalError(11.5.2.2) stopは,一つのスレッドから,他のスレッド又は指定されたスレッドグループ内のすべてのスレッドに影響するように呼び出されてもよい。
他のスレッドの任意の実行点で発生できるので,非同期的とする。
InternalErrorは,これから規定するメソッドstopを処理するのと同じ機構を使用して取り扱えるので,同様に非同期とする。
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,20.22)をルートとするクラス階層で組織化されている。
クラスException及びクラスErrorは,Throwableのサブクラスとする。
クラスRuntimeExceptionは,Exceptionの直接のサブクラスとする。
標準パッケージ,java.lang,java.util,java.io及びjava.netで宣言されている例外クラスは,標準例外クラス(standard exception class)と呼ぶ。
Javaプログラムは,throw文で既存の例外クラスを使用できる,又は,Throwable若しくはそのサブクラスのいずれかとして,付加的な例外クラスを適宜定義可能とする。
例外ハンドラに対するJavaコンパイル時検査の利点を得るために,大部分の新しい例外クラスは,検査例外クラスとして定義することが一般的である,特にRuntimeExceptionのサブクラスでないExceptionのサブクラスとして定義する。
Exception及びクラスRuntimeExceptionExceptionは,例外からの回復を希望する普通のプログラムのためのすべての例外のスーパクラスとする。
RuntimeExceptionは,クラスExceptionのサブクラスとする。
RuntimeExceptionのサブクラスは,非検査例外クラスとする。
パッケージjava.langは,次の標準実行時例外を定義する。これらは,パッケージjava.lang内の他のすべてのクラスのように,暗黙的にインポートされ,したがって単純名で参照可能とする。
ArithmeticException:除数ゼロでの整数除算(15.16.2)演算などの例外的な計算状況が発生した。
ArrayStoreException:配列の要素の型と代入互換でないクラスの値を配列要素に格納しようとした(10.10,15.25.1)。
ClassCastException:オブジェクトへの参照を不適切な型へキャスト(5.4,15.15)しようとした。
IllegalArgumentException:メソッドに,不当若しくは不適切な引数が渡された,又はメソッドが不適切なオブジェクトで呼び出された。このクラスは次のサブクラスを含む。
IllegalThreadStateException:要求された操作に対してスレッドが適切な状態になかった。
NumberFormatException:Stringを数値型の値へ変換しようとしたが,そのStringが適切な形式をもっていなかった。
IllegalMonitorStateException:スレッドが,ロックされていないオブジェクトを待機している他のスレッドを,待機(20.1.6,20.1.7,20.1.8),又は通知(20.1.9,20.1.10)しようとした。
IndexOutOfBoundsException:(配列,文字列若しくはベクトルを参照する)ある種のインデクス,又は二つのインデクス値若しくは一つのインデクス及び一つの長さのいずれかで規定される範囲が,領域を越えた。
NegativeArraySizeException:負の長さで配列を生成しようとした(15.9)。
NullPointerException:オブジェクト参照を要求されるところで,空参照をしようとした。
SecurityException:セキュリティ違反を検出した(20.17).。
java.utilは,次の付加的な標準非検査実行時例外を定義している。
java.util.EmptyStackException:空スタックの要素にアクセスしようとした。
java.util.NoSuchElementException:空ベクトルの要素にアクセスしようとした。
RuntimeException以外のExceptionの標準サブクラスは,すべて検査例外クラスとする。
パッケージjava.langは,次の標準例外を定義する。これらは,パッケージjava.lang内のすべての他のクラスと同様に,暗黙的にインポートされ,したがって単純名によって参照可能とする。
ClassNotFoundException:指定された名前をもつ,クラス又はインタフェースを見つけられなかった(20.3.8).。
CloneNotSupportedException:クラスObjectのメソッドclone(20.1.5)が,オブジェクトのクローンを作成するために呼び出されたが,そのオブジェクトのクラスは,インタフェースCloneableを実装していない。
IllegalAccessException:完全限定名を指定する文字列を使い,クラスをロードしようとしたが,現在実行中のメソッドは,指定したクラスの定義へのアクセスをもたない。その理由は,そのクラスは,publicでなく,かつ他のパッケージ内に存在しているからである。
InstantiationException:クラスClass内のメソッドnewInstanceを使用して,クラスのインスタンスを生成しようとしたが,それが,インタフェース,抽象的又は配列なので,指定されたクラスオブジェクトをインスタンス化できない。
InterruptedException:現在のスレッドは,待ち状態にあり,他のスレッドが,クラスThread(20.20.31)のメソッドinterruptを使用して,現在のスレッドに割り込んだ。
java.ioは,次の標準例外を定義している。
java.io.IOException:要求された入出力操作を正常に完了できなかった。このクラスのサブクラスには,次を含む。
java.io.EOFException:入力操作が正常に終了する前に,ファイルの終わりに達した。
java.io.FileNotFoundException:ファイル名文字列又はパス名で指定した名前をもつファイルが,ファイルシステム内で見つからなかった。
java.io.InterruptedIOException:現在のスレッドが,入出力操作の完了を待っているとき,他のスレッドが,クラスThread(20.20.31)のメソッドinterruptを使用して,現在のスレッドに割り込んだ。
java.io.UTFDataFormatException:要求された,Java修正版UTF-8形式への,又はからの文字列の変換が完了しなかった(22.1.15,22.2.14)。その理由は,文字列が長すぎる又は目的とするUTF-8データが Unicode をUTF-8に符号化した結果ではなかったためとする。
java.netは,次の付加的なjava.io.IOExceptionのサブクラスを定義している。
java.net.MalformedURLException:URL又はURLの一部として提供された文字列が,不適切な形式をもつ,又は不明の
プロトコルを指定した。
java.net.ProtocolException:ネットワークプロトコルが正しく実行されなかった。
java.net.SocketException:ソケットに関連する操作が,正常完了できなかった。
java.net.UnknownHostException:ネットワークホストの名前が,ネットワークアドレスに解決できなかった。
java.net.UnknownServiceException:ネットワーク接続が,要求されたサービスを提供できなかった。
ErrorError及びその標準サブクラスは,通常,普通のプログラムが回復を期待しない例外とする。クラスErrorは,クラス階層内のExceptionとは異なる,Throwableの別のサブクラスとする。プログラムは,回復が可能かもしれないすべての例外を捕捉するために,次の形式を使用できる。
} catch (Exception e) {
この記述は,回復が可能でないエラーを捕捉することはない。
パッケージjava.langは,ここで規定されるすべてのエラークラスを定義している。これらのクラスは,パッケージjava.lang内のすべての他のクラスと同様に,暗黙的にインポートされ,したがって単純名で参照可能とする。
LinkageErrorのサブクラスのインスタンスであるオブジェクトを投げる。
ClassFormatError,ClassCircularityError,及びNoClassDefFoundErrorを規定している。
IllegalAccessError,InstantiationError, NoSuchFieldError,NoSuchMethodError, IncompatibleClassChangeErrorのサブクラスのすべて,及びUnsatisfiedLinkErrorを含む。
VerifyErrorは,そこで規定している。
AbstractMethodErrorとする。
staticフィールドに対する初期化子の実行が,Error又はErrorのサブクラスではない例外を生じる場合,仮想計算機は,エラーExceptionInInitializerErrorを投げる。
VirtualMachineErrorのサブクラスのインスタンスであるオブジェクトを投げる。この言語規定及びJava仮想計算機規定は,次の仮想計算機エラーを定義している。
InternalError:仮想計算機を実装するソフトウェア内の欠陥,ホストシステムソフトウェア内の欠陥又はハードウェアの欠陥のために,内部エラーがJava仮想計算機内で発生した。このエラーは,検出されたときに非同期に配送され,Javaプログラムの任意の点で発生できる。
OutOfMemoryError:Java仮想計算機が,仮想メモリ又は物理メモリを使いつくし,自動記憶域管理が,オブジェクト生成要求を満足するのに十分なメモリを再利用できなかった。
StackOverflowError:通常,実行プログラム内の欠陥のために,スレッドが再帰呼出しを無制限に実行し,Java仮想計算機がスレッドに対するスタック空間を使いつくした。
UnknownError:例外又はエラーが発生したが,何らかの理由のためにJava仮想計算機が実際の例外又はエラーを報告できなかった。
OutOfMemoryErrorを取り扱って,回復を図る設計が,多分,オブジェクトの参照を注意深く無効にすることによって,なされることになろう。