実行時に発生する例外はプログラミングエラーに対して使用されます。この例外はJavaではjava.lang.RuntimeExceptionクラスで表現されています。このサブクラスには、パラメータが不正であることを意味するjava.lang.IllegalArgumentExceptionや配列の添え字が範囲外であることを意味するjava.lang.IndexOutOfBoundsExceptionや、メソッドを呼び出そうとした変数がnullを参照していたことを意味するjava.lang.NullPointerExceptionといったものがあります。実行時に発生する例外のほとんどは、メソッドのAPI仕様に従わずにメソッド呼び出しを行うこと(事前条件違反、precondition violation)が原因です。例えば、次のプログラムはコンパイルエラーにはなりませんが、実行するとエラーが発生します。
public class Sample320 { public static void main(String[] args) { int[] a = new int[4]; int i = 3; System.out.println("a[4]:"+a[i+1]); } }
配列aの要素へアクセスするためにはインデックスへは0からa.length-1の間の整数値しか使用できないという事前条件に対して違反をし、配列に存在しない要素へアクセスしようとしたためにエラーとなってしまうのです。
ここで次のように、java.lang.ArrayIndexOutOfBoundsExceptionをキャッチすることもできますが、本来は「System.out.println("a[4]:"+a[i+1]);」を無条件に実行するコーディングをしているのはプログラマのミスですから、このようなjava.lang.RuntimeExceptionを継承した例外のキャッチをするコーディングは推奨されません。条件文でi+1が4未満である場合に「System.out.println("a[4]:"+a[i+1]);」を実行するようにコードを修正するべきです。
public class Sample321 { public static void main(String[] args) { int[] a = new int[4]; int i = 3; try { System.out.println("a[4]:"+a[i+1]); } catch (java.lang.ArrayIndexOutOfBoundsException e) { System.out.println("エラー"); } } }
このように、「実行時に発生する例外」はプログラマのミスでエラーが発生しているということを教えてくれます。実際に出合ったときにはtry文を使って対処するのではなく、コードを修正して対応するということが基本になります。
JavaVMが実行不能になるようなエラーが発生した場合にはjava.lang.Errorを継承したクラスが使われます。java.lang.VirtualMachineErrorなどがあります。こういったエラーが発生するのはシステムが異常な状態になったときだけなので、通常のアプリケーションではキャッチするべきではありません。このため、「実行時に発生する例外」と同様にjava.lang.Errorについてはキャッチしなくてもコンパイル時にチェックされません。こういったことから分かるのは、通常のアプリケーション用プログラムをコーディングするうえでは、java.lang.Errorを使うことはないということです。ただし、java.lang.Errorによるエラーが発生した場合にはシステムの何かがおかしくなっているということだけは覚えておきましょう。
ここまでの説明から、「コンパイル時にチェックされない例外やエラー」を独自に実装するには、java.lang.Errorを継承する方法と、java.lang.RuntimeExceptionを継承する方法があるということに気が付くはずです。しかし、JavaVMが実行不能になるような異常な状態を表現する例外やエラーを独自に実装することは基本的にはないので、もし「コンパイル時にチェックされない例外やエラー」を独自に実装したいといった場合は、java.lang.RuntimeExceptionを継承するのが普通です。
ここまで「例外をキャッチする」という表現をしてきましたが、「キャッチする」に対応するのは「スローする(投げる)」になります。例外が発生するメソッドでは、どんな例外が発生するかが分かるように、どんな例外を投げるかについて宣言がされています。例えば、java.io.InputStreamReaderのcloseメソッドのAPIリファレンスを見ると、次のように記述されています。
public void close() throws IOException ストリームを閉じます。 定義: インターフェイス Closeable 内の close 定義: クラス Reader 内の close 例外: IOException - 入出力エラーが発生した場合
このことから、java.io.InputStreamReaderクラスのcloseメソッド内では、java.io.IOExceptionという例外が発生する可能性があることが分かります。また、APIリファレンスでのメソッドの宣言を見ても分かるように、プログラムでは、この例外が発生することを宣言するために、throws(投げる)というキーワードを使います。
今回はここまでです。次回は、独自の例外を定義する方法についてご紹介します。
小山博史(こやま ひろし)
情報家電、コンピュータと教育の研究に従事する傍ら、オープンソースソフトウェア、Java技術の普及のための活動を行っている。Ja-Jakartaプロジェクト(http://www.jajakarta.org/)へ参加し、コミッタの一員として活動を支えている。また、長野県の地域コミュニティである、SSS(G)(http://www.sssg.org/)やbugs(J)(http://www.bugs.jp/)の活動へも参加している。
Copyright © ITmedia, Inc. All Rights Reserved.