ランタイム系例外が発生するプログラムを幾つか挙げます。
ソースコードは以下の通り。
O301/O301.java
/** * ArrayIndexOutOfBoundsExceptionを確認します。 */ public class O301 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { int[] a = new int[10]; a[20] = 100; System.out.println("a[20]の値は " + a[20]); } }
実行結果は以下の通り。
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 20 at O301.main(O301.java:13)
13行目で例外 ArrayIndexOutOfBoundsExceptionが発生しています。ここでは配列の要素外を参照しようとしています。
ArrayIndexOutOfBoundsExceptionクラスについての詳細について気になる方は、説明書(API)を参照してください。Java SE 7(Java 1.7)の説明書(API)を掲示しておきます→ArrayIndexOutOfBoundsExceptionクラス。
ソースコードは以下の通り。
O302/O302.java
/** * StringIndexOutOfBoundsExceptionを確認します。 */ public class O302 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { String str = "Exception"; System.out.println("3~5文字目は " + str.substring(2, 5)); System.out.println("5~20文字目は " + str.substring(4, 20)); } }
実行結果は以下の通り。
3~5文字目は cep Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: 20 at java.lang.String.substring(Unknown Source) at O302.main(O302.java:14)
14行目で例外 StringIndexOutOfBoundsExceptionが発生しています。ここでは文字列の範囲外を参照しようとしています。
StringIndexOutOfBoundsExceptionクラスについての詳細について気になる方は、説明書(API)を参照してください。Java SE 7(Java 1.7)の説明書(API)を掲示しておきます→StringIndexOutOfBoundsExceptionクラス。
ソースコードは以下の通り。
O303/O303.java
/** * ArithmeticExceptionを確認します。 */ public class O303 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { int x; x = 3 / 0; System.out.println("xの値は " + x); } }
実行結果は以下の通り。
Exception in thread "main" java.lang.ArithmeticException: / by zero at O303.main(O303.java:13)
13行目で例外 ArithmeticExceptionが発生しています。ここではゼロで割り算をしようとしています。
ArithmeticExceptionクラスについての詳細について気になる方は、説明書(API)を参照してください。Java SE 7(Java 1.7)の説明書(API)を掲示しておきます→ArithmeticExceptionクラス。
ソースコードは以下の通り。
O304/O304.java
(15行目に黄線が生じますが問題ありません)
import java.awt.Point; /** * NullPointerExceptionを確認します。 */ public class O304 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Point p = null; p.x = 100; } }
実行結果は以下の通り。
Exception in thread "main" java.lang.NullPointerException at O304.main(O304.java:15)
15行目で例外 NullPointerExceptionが発生しています。参照先が無い(null)変数の参照先インスタンスにアクセスしようとしています。
NullPointerExceptionクラスについての詳細について気になる方は、説明書(API)を参照してください。Java SE 7(Java 1.7)の説明書(API)を掲示しておきます→NullPointerExceptionクラス。
メソッドを呼び出している最中に例外が発生したらどうなるでしょうか。
ソースコードは以下の通り。
O305/O305.java
import java.awt.Point; /** * メソッド呼び出し時の動きを確認します。 */ public class O305 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Point p = new Point(10, 20); Point q = null; Point r = new Point(30, 50); System.out.println("変数 pを引数として呼び出します。"); O305.print(p); System.out.println("変数 qを引数として呼び出します。"); O305.print(q); System.out.println("変数 rを引数として呼び出します。"); O305.print(r); System.out.println("プログラムは正常に終了しました。"); } /** * Point型の内容を表示します。 * @param point ポイント */ private static void print(Point point) { System.out.println("メソッドが呼び出されました。"); System.out.println("xの値は " + point.x); System.out.println("yの値は " + point.y); System.out.println("メソッドは正常に終了しました。"); } }
実行結果は以下の通り。
変数 pを引数として呼び出します。 メソッドが呼び出されました。 xの値は 10 yの値は 20 メソッドは正常に終了しました。 変数 qを引数として呼び出します。 メソッドが呼び出されました。 Exception in thread "main" java.lang.NullPointerException at O305.print(O305.java:32) at O305.main(O305.java:20)
「変数 qを引数として呼び出します。」の表示に続いて、「メソッドが呼び出されました。」の表示をした後、プログラムが強制終了していることが分かります。変数 rについては何も表示されずに、プログラムは強制終了しています。
try catch構文を利用して、プログラムが強制終了されないようなプログラムを作ってみます。例外 ArrayIndexOutOfBoundsExceptionが発生する可能性のあるプログラムに try catch構文を使用してみました。
ソースコードは以下の通り。
O306/MySystem.java
(ライブラリをそのまま利用します)
O306/O306.java
/** * try catch構文を確認します。 */ public class O306 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; System.out.println("2013年の月の日数を表示します。"); int month = MySystem.in.getInt("月を入力してください"); try { int day = months[month - 1]; System.out.println(month + "月は" + day + "日です。"); } catch(ArrayIndexOutOfBoundsException e) { System.out.println("配列の要素外を参照しました。"); } System.out.println("プログラムを終了します。"); } }
実行結果の例は以下の通り。
2013年の月の日数を表示します。 月を入力してください? 8 8月は31日です。 プログラムを終了します。
2013年の月の日数を表示します。 月を入力してください? 20 配列の要素外を参照しました。 プログラムを終了します。
プログラムが正常終了していることが分かります。
「20」と入力した場合、19行目の処理中に例外 ArrayIndexOutOfBoundsExceptionが発生するものの、tryブロックの中にあるため catchに飛びます。対応する ArrayIndexOutOfBoundsExceptionが 21行目に見つかったため、22行目を実行します。その後は、25行目を実行してプログラムは正常終了します。
O305プロジェクトのソースコードを改良し、メインメソッドに try catch構文を適用してみた例は以下になります。
O307/O307.java
import java.awt.Point; /** * メソッド呼び出し時の try catch構文の動きを確認します。 */ public class O307 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Point p = new Point(10, 20); Point q = null; Point r = new Point(30, 50); try { System.out.println("変数 pを引数として呼び出します。"); O307.print(p); System.out.println("変数 qを引数として呼び出します。"); O307.print(q); System.out.println("変数 rを引数として呼び出します。"); O307.print(r); } catch(NullPointerException e) { System.out.println("NullPointerExceptionが発生しました。"); } System.out.println("プログラムは正常に終了しました。"); } /** * Point型の内容を表示します。 * @param point ポイント */ private static void print(Point point) { System.out.println("メソッドが呼び出されました。"); System.out.println("xの値は " + point.x); System.out.println("yの値は " + point.y); System.out.println("メソッドは正常に終了しました。"); } }
実行結果は以下の通り。
変数 pを引数として呼び出します。 メソッドが呼び出されました。 xの値は 10 yの値は 20 メソッドは正常に終了しました。 変数 qを引数として呼び出します。 メソッドが呼び出されました。 NullPointerExceptionが発生しました。 プログラムは正常に終了しました。
変数 qについて処理中に、37行目で例外 NullPointerExceptionが発生します。この printメソッドの中では例外を捕捉していませんが、呼び出し元である 21行目は tryブロックの中にあり、今回の例外に対応する catch句ということで 25行目が実行され、その後、28行目が実行されてプログラムは正常終了します。
このように、メソッドの中で例外が発生した場合には、メソッドを呼び出した側を順番に遡って tryブロックの中ではないか、また、tryブロックの中にあった場合には発生した例外に対応する catchが無いかを、順次調べていくことになります。
なお、今回も変数 rの内容は表示されませんでした。tryブロックの中に例外が発生した場合、catchに移行したとしても、再び tryブロックの中に戻る方法はありません。今回の場合は、例えば、変数 p, q, rそれぞれに try catch構文を用いると、結果的に、変数 rの内容を表示させることができるようになります。具体的には以下のような感じです。
try { System.out.println("変数 pを引数として呼び出します。"); O307.print(p); } catch(NullPointerException e) { System.out.println("NullPointerExceptionが発生しました。"); } try { System.out.println("変数 qを引数として呼び出します。"); O307.print(q); } catch(NullPointerException e) { System.out.println("NullPointerExceptionが発生しました。"); } try { System.out.println("変数 rを引数として呼び出します。"); O307.print(r); } catch(NullPointerException e) { System.out.println("NullPointerExceptionが発生しました。"); }
予約語 catchの後ろにある eは変数名です。eは 例外クラス(Exceptionクラスを継承するクラス)のインスタンスになりますが、デバッグ作業に便利なprintStackTraceメソッドを持っています。ここでは、それを使用してみます。
O308/O308.java
import java.awt.Point; /** * printStackTraceメソッドの動きを確認します。 */ public class O308 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Point p = new Point(10, 20); Point q = null; Point r = new Point(30, 50); try { System.out.println("変数 pを引数として呼び出します。"); O308.print(p); System.out.println("変数 qを引数として呼び出します。"); O308.print(q); System.out.println("変数 rを引数として呼び出します。"); O308.print(r); } catch(NullPointerException e) { System.out.println("NullPointerExceptionが発生しました。"); e.printStackTrace(); } System.out.println("プログラムは正常に終了しました。"); } /** * Point型の内容を表示します。 * @param point ポイント */ private static void print(Point point) { System.out.println("メソッドが呼び出されました。"); System.out.println("xの値は " + point.x); System.out.println("yの値は " + point.y); System.out.println("メソッドは正常に終了しました。"); } }
実行結果は以下の通り。
変数 pを引数として呼び出します。 メソッドが呼び出されました。 xの値は 10 yの値は 20 メソッドは正常に終了しました。 変数 qを引数として呼び出します。 メソッドが呼び出されました。 NullPointerExceptionが発生しました。 java.lang.NullPointerException at O308.print(O308.java:38) at O308.main(O308.java:21) プログラムは正常に終了しました。
printStackTraceメソッドを利用することで、プログラムが強制終了したときに発生するようなメッセージが表示されています。デバッグ作業をする際などに使用すると便利です。
catchのところで指定する例外は、実際の例外の型ではなくて、そのスーパークラス側のクラスを指定することができます。スーパークラス側を指定すると、いろいろな種類の例外を同じ catchで捕捉することができるようになりますが、一方で、どんな例外が発生したのかが分かりづらくなります。適当な範囲で対応、ということになります。
O309/O309.java
import java.awt.Point; /** * メソッド呼び出し時の try catch構文の動きを確認します。 */ public class O309 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Point p = new Point(10, 20); Point q = null; Point r = new Point(30, 50); try { System.out.println("変数 pを引数として呼び出します。"); O309.print(p); System.out.println("変数 qを引数として呼び出します。"); O309.print(q); System.out.println("変数 rを引数として呼び出します。"); O309.print(r); } catch(Exception e) { System.out.println("例外が発生しました。"); } System.out.println("プログラムは正常に終了しました。"); } /** * Point型の内容を表示します。 * @param point ポイント */ private static void print(Point point) { System.out.println("メソッドが呼び出されました。"); System.out.println("xの値は " + point.x); System.out.println("yの値は " + point.y); System.out.println("メソッドは正常に終了しました。"); } }
実行結果は以下の通り。
変数 pを引数として呼び出します。 メソッドが呼び出されました。 xの値は 10 yの値は 20 メソッドは正常に終了しました。 変数 qを引数として呼び出します。 メソッドが呼び出されました。 例外が発生しました。 プログラムは正常に終了しました。
今回は Exceptionクラスを指定していますが、上手く捕捉できています。
なお、Javaでは今回の例のように、実際に投げられるインスタンスよりスーパークラス側のクラスで捕捉できることができます。そのため、複数のcatchを使用した場合、正しい順番を指定する必要がある場合がありますが、解説が複雑になるため省略します。
メソッド内で例外を処理しない場合には、予約語throwsを使用して、そのメソッドが例外を投げる可能性があることを明示することができます。ランタイム系例外の場合は、記述しても記述しなくても構いません。
ソースコードは以下の通り。
O310/O310.java
import java.awt.Point; /** * throwsの使用法を確認します。 */ public class O310 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Point p = new Point(10, 20); Point q = null; Point r = new Point(30, 50); try { System.out.println("変数 pを引数として呼び出します。"); O310.print(p); System.out.println("変数 qを引数として呼び出します。"); O310.print(q); System.out.println("変数 rを引数として呼び出します。"); O310.print(r); } catch(NullPointerException e) { System.out.println("NullPointerExceptionが発生しました。"); } System.out.println("プログラムは正常に終了しました。"); } /** * Point型の内容を表示します。 * @param point ポイント * @throws NullPointerException 引数が nullのとき発生する */ private static void print(Point point) throws NullPointerException { System.out.println("メソッドが呼び出されました。"); System.out.println("xの値は " + point.x); System.out.println("yの値は " + point.y); System.out.println("メソッドは正常に終了しました。"); } }
実行結果は以下の通り。
変数 pを引数として呼び出します。 メソッドが呼び出されました。 xの値は 10 yの値は 20 メソッドは正常に終了しました。 変数 qを引数として呼び出します。 メソッドが呼び出されました。 NullPointerExceptionが発生しました。 プログラムは正常に終了しました。
36行目に throwsを記述しました。ランタイム系例外の場合は、記述しても記述しなくても構いません。
また、あるメソッドが何種類かの例外から 1つの例外を投げるような場合には、throwsの後に例外名をコンマで区切って記述することができます。具体的には以下のような感じです。
private void method() throws Sample1Exception, Sample2Exception, Sample3Exception { ; }