例外の種類

Revised: May/4th/2005: Since: Jan./1st/2002

例外はオブジェクトとして扱われます。例外が発生すると、その種類に応じたクラス型のオブジェクトが生成されます。例外処理とは、このオブジェクトを捕まえて、処理することに他なりません。

例外クラス

全ての例外オブジェクトのクラス型は、Throwable クラスをスーパークラスに持ちます。その継承階層に従って、三つの種類に分類できます。

java.lang.Object
  |
  +--java.lang.Throwable
       |
       +--java.lang.Error                  <- 例外処理任意。エラー。
       |
       +--java.lang.Exception
            |
            +--java.lang.RuntimeException  <- 例外処理任意。実行時例外。
            |
            +--そのほか                    <- 例外処理必須。チェック例外(検査例外)。

全ての例外クラスは Throwable を継承します。この中で、ErrorRuntimeException およびそれらを継承するものは例外処理が任意です。これらの例外クラスを非検査例外非チェック例外 (unchecked exception) と呼びます。非チェック例外を除いた残りの例外クラスは全て例外処理が必須です。これらの例外クラスを検査例外チェック例外 (checked exception) と呼びます。

非チェック例外(非検査例外)の中で、Error を継承したものは、JVM や OS に依存するエラーであり、正常なプログラムであっても、いつでも発生する可能性があり、発生後にプログラムの中でハンドリングすることは困難です。一般に、例外処理のコードは追加しません。

もう一つの非チェック例外である、RuntimeException を継承する実行時例外は、デバッグ済みのプログラムであれば発生しないであろうものです。通常、非チェック例外と言ったときには、これを指します。

一方、非チェック例外以外の全ての例外クラスであるチェック例外(検査例外)は、コンパイル時に例外処理が実装されているかどうか検査され、含んでいない場合はコンパイルエラーとします。

新規の例外クラスを追加する場合、例外処理によって例外的事象から回復したい場合は、Exception を継承して作ります。特に非チェック例外としたい場合は、RuntimeException を継承します。

Error

java.lang.Object
  |
  +--java.lang.Throwable
        |
        +--java.lang.Error

このクラスをスーパークラスとして派生する系列の例外は、致命的なエラーです。アプリケーション側で処理できない JavaVM で検出されるようなエラーです。具体的には、メモリ不足、AWTエラー、スレッド・エラーなどに対応するクラスが派生しています。

Java プログラムで例外処理しようにも、プログラムの実行の継続ができないので、例外処理は不可能です。

エラーの例

次の例は、コレクションクラス java.util.ArrayList に要素を無限に追加するものです。JDK 1.5 以上で動作します。要素が無限に追加されるので、java.lang.OutOfMemoryError が発生しています。メモリ不足のためにオブジェクトを割り当てることができなくなったときにスローされます。

ここで使っている try-catch 文は明示的な例外処理の構造です。try ブロック内で例外が発生すると、 catch ブロックに制御が移ります。後続の節で詳しく説明します。

import java.util.ArrayList;
import java.util.List;

class ErrorDemo {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		// SDK 1.4 の場合
		// List list = new ArrayList();
		int count = 0;
		try {
			while (true) {
				count++;
				list.add("Hello");
			}
		} catch (OutOfMemoryError e) {
			System.out.println(count);
			e.printStackTrace();
		}
	}
}
C:\java>javac ErrorDemo.java

C:\java>java ErrorDemo
7634069
java.lang.OutOfMemoryError: Java heap space

RuntimeException

java.lang.Object
  |
  +--java.lang.Throwable
        |
        +--java.lang.Exception
              |
              +--java.lang.RuntimeException

Exception クラスから派生しています。今までに学習した範囲では、実際に遭遇する例外の殆どがこのクラスから派生しています。具体的には、配列要素数、文字列の数値への変換、ゼロ除算などに対応する例外クラスのスーパークラスです。

このクラスの系列に入る例外は、正常なプログラムでは発生しないはずのものであり、例外処理のロジックを組み込むことは必須ではありません。

RuntimeException を継承した、例外処理が必須でない例外を、非検査例外/非チェック例外 (unchecked exceptions classes) と呼びます。

ゼロ除算などで発生する算術例外 ArithmeticException、不正な引数、または不適切な引数をメソッドに渡したことを示すためにスローする IllegalArgumentException、null が代入された参照を使おうとすると発生する NullPointerException、不正なインデックスを使って配列がアクセスされたことを示す ArrayIndexOutOfBoundsException などが代表的です。

非チェック例外の例

次の例は、コマンドライン引数を出力するものです。引数を指定しないで実行すると、例外 java.lang.ArrayIndexOutOfBoundsException が発生します。

class RuntimeExceptionDemo {
	public static void main(String[] args) {
		System.out.println(args[0]);
	}
}
C:\java>javac RuntimeExceptionDemo.java

C:\java>java RuntimeExceptionDemo
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
        at RuntimeExceptionDemo.main(RuntimeExceptionDemo.java:3)

Exception

java.lang.Object
  |
  +--java.lang.Throwable
        |
        +--java.lang.Exception

RuntimeException クラス以外の Exception クラスから派生する例外は、ロジックで回避することが難しいものです。具体的には、入出力異常、ファイルが見つからない、クラス/メソッドが見つからないなどに対応する例外のスーパークラスです。

このクラスの系列に含まれる例外が発生しそうな処理については、 try{}catch(){} ブロックを用いた例外処理が必須です。これがない場合は、コンパイルエラーになります。

RuntimeException 以外の Exception を継承した、例外処理が必須である例外を、検査例外/チェック例外 (checked exceptions classes) と呼びます。

チェック例外の例

次の例は、コアパッケージ java.io を用いて、テキストファイル "test_file.txt" の中身を読み取るものです。指定したファイル "test_file.txt" を用意しない場合は、チェック例外 java.io.FileNotFoundException と非チェック例外 java.lang.NullPointerException が発生します。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

class ExceptionDemo {
	public static void main(String[] args) {
		FileReader in = null;
		try {
			System.out.println("ファイル・オブジェクトの作成");
			File inFile = new File("test_file.txt");
			System.out.println("入力ストリームの作成");
			in = new FileReader(inFile);	// inFile がないので例外発生
			System.out.println("読み込み");
			int c = 0;
			while ((c = in.read()) != -1) {
				System.out.write(c);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				System.out.println("入力ストリームのクローズ");
				in.close();	// in が初期化されていないので例外発生
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
C:\java>javac ExceptionDemo.java

C:\java>java ExceptionDemo
ファイル・オブジェクトの作成
入力ストリームの作成
java.io.FileNotFoundException: test_file.txt (指定されたファイルが見つかりません。)
        at java.io.FileInputStream.open(Native Method)
        at java.io.FileInputStream.<init>(Unknown Source)
        at java.io.FileReader.<init>(Unknown Source)
        at ExceptionDemo.main(ExceptionDemo.java:12)
入力ストリームのクローズ
Exception in thread "main" java.lang.NullPointerException
        at ExceptionDemo.main(ExceptionDemo.java:23)


Copyright © 2001-2005 SUGAI, Manabu. All Rights Reserved.