• 検索結果がありません。

OutOfBoundExample.java ( 配列の添字オーバーの例外 )

ドキュメント内 i I Java Java Java (ページ 192-196)

第 2 章 Java 言語の基本的な文法 I 5

3.47 OutOfBoundExample.java ( 配列の添字オーバーの例外 )

public class DivideZeroExample {

public static void main(String[] args) { int x = 0;

int y = 10;

int z = y / x;

System.out.printf( "%d / %d = %d%n", y, x, z );

} }

実行させると、次なるエラーメッセージが表示されます。

Exception in thread "main" java.lang.ArithmeticException: / by zero at section0306.DivideZeroExample.main(DivideZeroExample.java:6) このメッセージを解読してみると、

「main」というスレッドにおいての「0による除算」という例外java.lang.ArithmeticExceptionが、パッ ケージsection0306のプログラムDivideZeroExample.javaのmainメソッド(DivideZeroExample.java の6 行目)において発生したよ!

スレッドとは、プログラムの流れのことです。私たちがここまで作ってきたプログラムは「シングルスレッド(single

thread)」プログラムと言って、プログラムが最初から最後まで1 本の流れ、mainスレッド、として実行されます。一

方、Java では「マルチスレッド(multi thread)」プログラムと言って、複数の流れが同時に進行する並列プログラム を作ることが出来ます。スレッドについては後に書くつもりですが、この授業では触れられませんので、各自で自習し てみて下さい。

次のプログラムも、よくやる配列の添字操作のミスです。

ソースコード3.47 OutOfBoundExample.java (配列の添字オーバーの例外) package section0306;

public class OutOfBoundExample {

public static void main(String[] args) { int[] array = new int[10];

for(int i=1; i<=array.length; i++) { array[i] = i;

}

for(int x : array) {

System.out.print( x + " " );

}

System.out.println();

} }

この場合のエラーメッセージは、

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10 at section0306.OutOfBoundExample.main(OutOfBoundExample.java:6)

6 行目で、java.lang.ArrayIndexOutOfBoundsException なる例外が起こったと言っています。java.langパッ ケージ内の ArrayIndexOutOfBoundsException クラスのインスタンスが投げられたんですね。コロンの後ろの 10 は何か?というと、配列の添字番号の限度9を超えて 10が設定されたぞ!という意味です。

これらのプログラムは、いずれもコンパイルエラーは起きません。つまり、文法的には間違っていない。Java のコ ンパイラは残念ながらこのレベルのチェックはしないのです。実行してみて初めてエラーに気づく。このときJVMは

Exceptionクラスのサブクラスのインスタンスを作成し、それをプログラムに投げて きます。どちらのプログラムも、

それを受け取る処理 (catch文)が無いので、コンソールにエラーメッセージを吐き出した後、強制終了されています。

3.6.2 Exception クラス

実行時に投げられる例外は、全て java.lang.Exception クラスと java.lang.Errorクラスのいずれかをスー パークラスとしたインスタンスになります(図3.6.2参照:この図はほんの一部、Error クラスのサブクラスは書いて いません)。

java.lang.Object java.lang.Throwable

java.lang.Exception

java.lang.RuntimeException

java.lang.Error

java.io.IOException

java.lang.IndexOutOfBoundsException java.lang.ArrayIndexOutOfBoundsException java.io.FileNotFoundException

java.lang.NullPointerException java.lang.ArithmeticException

図3.10 Exceptionとは

まず、Errorクラス系の例外が発生すると、プログラムでは回復が不可能な問題が起こっています。

• java.lang.OutOfMemoryError:必要なメモリーが確保できなかった

• java.lang.StackOverflowError:再帰呼び出しなどによりスタック領域が足りなくなった

などがError クラスのサブクラスとなり、これらのインスタンスが投げられたら、ハードウェアまでも考慮した解決

法を考えなければいけません(ソフトでは対処しきれない)。

一方、Exception系の例外が発生した場合は、2種類に分類して考えます。

非検査例外:java.lang.RuntimeExceptionクラスのサブクラス

検査例外:それ以外のサブクラス

非検査例外には、先に出てきたArithmeticExceptionやArrayIndexOutOfBoundsExceptionが含まれます。プ ログラムの多くの場所で発生する可能性があるので、例外発生時の処理 (catch 文など)を記述しないことが許されて いる例外です。とはいえ、例外が起こりそうなところでは、何らかの処理をしなければ当然、強制終了されます。

一方、検査例外には、読み込もうとしたファイルが存在しない際のFileNotFoundException などが含まれます。

これは、明らかに例外が起こる可能性のある場所が事前にわかるので、必ず対処の処理を書かなければいけません(書 かないと文法エラー)。

例外が起こった場所を特定し、どんな例外が起こったかを突き止め、その解決処理を書くわけですが、投げられた 例外はそれに役立つ「スタックトレース (stack trace)」という情報を持っているので、まずはそれを出力させてみま しょう。

ソースコード3.48 () DivideZeroExample.java (スタックトレース) package section0306;

public class DivideZeroExample {

public static void main(String[] args) { int x = 0;

int y = 10;

try {

int z = y / x;

System.out.printf( "%d / %d = %d%n", y, x, z );

} catch( Exception e ) { e.printStackTrace();

} } }

まず、例外が発生しそうな部分をtry ブロック「try { }」で囲みます。上のプログラムの場合、6行目から9 行目までがtryブロックですね。そして、tryブロックに続いてcatch ブロック「catch( 受け取る例外の型 変数 ) {  }」を置きます。上のプログラムの場合、9行目から 11行目がcatchブロックです。catch 文の小カッコ 内には、投げられた例外を受け取るための引数を型宣言しながら置きます。

2 つのブロックは連続しないといけませんが、tryブロックとcatch ブロックの間を以下のように改行して書く方 式もあります。このテキストでは行数の節約もあるので、前者で書くことにします。

try {

} // 改行してから catch ブロック catch( Exception e ) {

}

プログラム中のtryブロック内で例外が発生すると、catchブロックに制御が移動し、投げられた例外インスタンス がその引数と一致したとき、その中で問題を解決してプログラムを続行させるか、プログラムを終了させるかを決めま

す。catch ブロックで受け取る例外のクラスは正確(?)なクラスでも良いのですが、スーパークラスで受け取るように

書けば、そのサブクラスの例外全てを受け取ることができます。上のプログラムでは、Exception型として受け取っ ています。従ってException系のどんな例外でも引き受けるcatch ブロックということになります(逆に言うと、手 抜き!?)。

Exception型インスタンスには、スタックトレース情報を標準出力してくれるメソッドprintStackTrace() があ るので、ひとまず、それを使っています。でも、これでは強制終了されただけと何ら変わりは無いので、ちょっとだけ 対処したプログラムに換えましょう。

ソースコード3.49 () DivideZeroExample.java package section0306;

public class DivideZeroExample {

public static void main(String[] args) { int x = 0;

int y = 10;

try {

int z = y / x;

System.out.printf( " %d / %d = %d%n", y, x, z );

} catch( ArithmeticException e ) {

System.err.println( "割り算で分母が 0 になりました。" );

System.err.println( "計算アルゴリズムが正しいかのチェックをお願いします。" );

} } }

今度は、起こり得る正しい(?)例外ArithmeticException で受け取るようにしました。従って、それ以外の例外が 起こった場合は受け損なって強制終了されます。

ソースコード3.50 (改) OutOfBoundExample.java package section0306;

import java.util.Arrays;

public class OutOfBoundExample {

public static void main(String[] args) { int[] array = new int[10];

try {

for(int i=1; i<=array.length; i++) { array[i] = i;

}

} catch( ArrayIndexOutOfBoundsException e ) {

System.err.println( "配列の範囲外のアクセスが行われました" );

}

System.out.println( Arrays.toString( array ) );

} }

こちらは、ArrayIndexOutOfBoundsException を受け取るようにしました。この場合、正常に配列の初期化が終了

すれば、catchブロックは飛び越して配列の要素が標準出力されます。いずれのプログラムも、例外を無理に発生させ

ている例ですが、そこは目をつぶりましょう。(^^;)

さて、tryブロックにおいて何種類かの例外が投げられる可能性もありますよね、そんな時はcatch ブロックを複数 並べて書くか、Java 7から導入された「複数例外catch」を使います。

ソースコード3.51 (改2) DivideZeroExample.java

ドキュメント内 i I Java Java Java (ページ 192-196)