try-catch の例

Revised: May/4th/2005: Since: Jan./27th/2002

ここでは単純な例外処理のサンプルを示します。

ArrayIndexOutOfBoundsException

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

不正なインデックスを使って配列がアクセスされたことを示します。

class TestExcep1 {
	public static void main(String[] args) {
		System.out.println("実行開始。");
		try {
			System.out.println(args[0]);
			System.out.println("tryブロック終了");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を一つ入力してください。");
		}
		System.out.println("実行終了。");
	}
}
C:\java>javac TestExcep1.java
C:\java>java TestExcep1
実行開始。
例外:java.lang.ArrayIndexOutOfBoundsException
引数を一つ入力してください。
実行終了。

引数の配列の個数が正しくないために起こった例外です。この場合は、ロジックで容易に回避できます:

if (args.length != 1) {
	System.out.println("引数を一つ入力してください。");
	return;
}

配列 args[] の要素数が1でない場合に true となり、ブロック内が実行されます。

NumberFormatException

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

文字列を数値型に変換しようとしたとき、文字列の形式が正しくない場合にスローされます。次の例は、Jakarta Commons の Validator というパッケージに含まれるコードの断片を加工したものです。

class NumberFormatExceptionDemo {
	public static Integer formatInt(String value) {
		if (value == null) {
			return null;
		}
		try {
			return new Integer(value);
		} catch(NumberFormatException e) {
			e.printStackTrace();
			return null;
		} finally {
			System.out.println("finallyブロックは必ず実行");
		}
	}
	public static void main(String[] args) {
		if (formatInt(args[0]) != null) {
			System.out.println(args[0] + "は整数です。");
		} else {
			System.out.println(args[0] + "は整数ではありません。");
		}
	}
}

ここでは try-catch 構文finally ブロックを含めています。例外が発生してもしなくても、必ず実行されます。コンピュータの制御の実行順序としては、return 文よりも後に到達していることに注意してください。戻り値型が指定されている場合でも、finally のなかで return する必要はありません。もし finally の中で return すると、先に実行された return 文が上書きされます。

C:\java>javac NumberFormatExceptionDemo.java

C:\java>java NumberFormatExceptionDemo 1
finallyブロックは必ず実行
1は整数です。

C:\java>java NumberFormatExceptionDemo 1.1
java.lang.NumberFormatException: For input string: "1.1"
        at java.lang.NumberFormatException.forInputString(Unknown Source)
        at java.lang.Integer.parseInt(Unknown Source)
        at java.lang.Integer.(Unknown Source)
        at NumberFormatExceptionDemo.formatInt(NumberFormatExceptionDemo.java:7)

        at NumberFormatExceptionDemo.main(NumberFormatExceptionDemo.java:16)
finallyブロックは必ず実行
1.1は整数ではありません。

ArithmeticException

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

算術計算で例外的条件が発生した場合にスローされます。たとえば、整数を「ゼロで除算」するとこのクラスのインスタンスがスローされます。

class TestExcep2 {
	public static void main(String[] args) {
		System.out.println("実行開始。");
		try{
			int i, j=100;
			System.out.println("j: " + j);
			i=Integer.parseInt(args[0]);
			System.out.println("j/i: " + j/i);
				//0で割ると例外がスローされる
			System.out.println("tryブロック終了");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を一つ入力してください。");
		} catch (NumberFormatException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を整数で入力してください。");
		} catch (ArithmeticException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を0以外の整数で入力してください。");
		} finally {
			System.out.println("finallyブロックは必ず実行");
		}
		System.out.println("実行終了。");
	}
}

引数が与えられない場合は、最初の catch ブロックにつかまります。引数は与えられるけど、整数に変換できない場合は、二番目の catch ブロックにつかまります。引数が与えられて整数に変換できるけど、それがゼロであった場合は、ゼロ割りになって三番目の catch ブロックにつかまります。

C:\java>javac TestExcep2.java

C:\java>java TestExcep2
実行開始。
j: 100
例外:java.lang.ArrayIndexOutOfBoundsException: 0
引数を一つ入力してください。
finallyブロックは必ず実行
実行終了。

C:\java>java TestExcep2 0
実行開始。
j: 100
例外:java.lang.ArithmeticException: / by zero
引数を0以外の整数で入力してください。
finallyブロックは必ず実行
実行終了。

C:\java>java TestExcep2 -5
実行開始。
j: 100
j/i: -20
tryブロック終了
finallyブロックは必ず実行
実行終了。

Exception

例外オブジェクトの代入互換性

発生する例外を全てキャッチしたい場合は、 Exception 型でキャッチすることができます。Errorまで含めた全てのスーパークラスは Throwable です。~able という識別子は、インタフェースの命名規則に多いのですが、Throwable はクラスです。

全ての例外クラスは Exception クラスをスーパークラスに持ちます。したがって、全ての例外クラスを Exception 型として catch できるのです。

class TestExcep4 {
	public static void main(String[] args) {
		String str=null;
		try {
			System.out.println(str.length());
		} catch (Exception e) {
			System.out.println("例外:" + e);
		}
	}
}

このサンプルでは、 null 値が代入された文字列型オブジェクトに length() メソッドを要求しています。文字列が代入されていれば、文字の個数を返しますが、 null 値の場合は、例外 NullPointException が発生します。

C:\java>javac TestExcep4.java

C:\java>java TestExcep4
例外:java.lang.NullPointerException

このサンプルでは、 NullPointerException という例外が発生していますが、 Exception 型の例外としてきちんと拾われています。全ての例外は Exception から派生しているので、自動型変換されてキャッチされるのです。

キャッチ順序

Exception 型は全ての例外オブジェクトと代入互換なので、最後に記述する必要があります。Exception 型を最初に記述してしまうと、全ての例外オブジェクトがそこでキャッチされてしまうので、後続の catch ブロックには絶対に制御が移らないのです。SDK 1.4 以上では、Exception を先にキャッチするとコンパイルエラーとなります。

class ExceptionCatchDemo {
	public static void main(String[] args) {
		try {
			Object obj = new Integer(0);
			String str = (String)obj; 
		} catch (Exception e) {
			e.printStackTrace();
		} catch (ClassCastException e) {
			System.out.println("例外処理中。キャストが失敗しました。");
			e.printStackTrace();
		} finally {
			System.out.println("実行終了");
		}
	}
}
C:\java>javac ExceptionCatchDemo.java
ExceptionCatchDemo.java:8: 例外 java.lang.ClassCastException はすでにキャッチさ
れています。
                } catch (ClassCastException e) {
                  ^
エラー 1 個

この場合は、ExceptionとClassCastExceptionのキャッチ順序を入れ替えると、コンパイルが正常に通るようになります。ここでは、代入互換性のない Integer型からString型にキャストしようとしているため、ClassCastExceptionが発生しています。Integer型からString型への変換は、キャストではなく、Integer#toString() を使う必要があります。

C:\java>javac ExceptionCatchDemo.java

C:\java>java ExceptionCatchDemo
例外処理中。キャストが失敗しました。
java.lang.ClassCastException: java.lang.Integer
        at ExceptionCatchDemo.main(ExceptionCatchDemo.java:5)
実行終了

まとめ

class TestExcep2 {
	public static void main(String[] args) {
		System.out.println("実行開始。");
		try{
			int i, j=100;
			System.out.println("j: " + j);
			i=Integer.parseInt(args[0]);
			System.out.println("j/i: " + j/i);
				//0で割ると例外がスローされる
			System.out.println("tryブロック終了");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を一つ入力してください。");
		} catch (NumberFormatException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を整数で入力してください。");
		} catch (ArithmeticException e) {
			System.out.println("例外:" + e);
			System.out.println("引数を0以外の整数で入力してください。");
		} catch (Exception e) {
			System.out.println("なんらかの例外が発生しました。");
			e.printStackTrace();
		} finally {
			System.out.println("実行終了。");
		}
	}
}
C:\java>javac TestExcep2.java

C:\java>java TestExcep2
実行開始。
j: 100
例外:java.lang.ArrayIndexOutOfBoundsException: 0
引数を一つ入力してください。
実行終了。

C:\java>java TestExcep2 0
実行開始。
j: 100
例外:java.lang.ArithmeticException: / by zero
引数を0以外の整数で入力してください。
実行終了。

C:\java>java TestExcep2 a
実行開始。
j: 100
例外:java.lang.NumberFormatException: For input string: "a"
引数を整数で入力してください。
実行終了。

C:\java>java TestExcep2 1
実行開始。
j: 100
j/i: 100
tryブロック終了
実行終了。


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