例外(Java)
例外
はじめに
ここでは、例外(Exception)処理について説明します。例外とtry〜catch
Javaの実行時エラーには、エラー(Error)と例外(Exception)があり、前者はプログラムの停止に至る「致命的なエラー」、後者は実行時例外(プログラムの実行時に発生するエラー)などの対処可能なエラーを意味します。たとえば、よくある「配列あふれ」の場合には、java.lang.ArrayIndexOutOfBoundsExceptionという例外が発生します。ためしに配列をあふれさせてみましょう。Sample018を作ってみましょう。
package test.basic;
public class Sample018 {
// int型の3の要素をもつ配列の宣言
static int[] i1 = new int[3];
public static void main(String[] args) {
for(int i=0;i<10;i++){
arrayAdd(i,i);
System.out.println(Integer.toString(i));
}
}
private static void arrayAdd(int i, int value){
i1[i]=value;
}
}
このプログラムは「3つしか用意していない配列に10個の要素をいれる」という全く間違ったものですね。ためしに実行してみてください。以下のようなメッセージがコンソールにでましたでしょうか?
0
1
2
java.lang.ArrayIndexOutOfBoundsException: 3
at test.basic.Sample018.arrayAdd(Sample018.java:24)
at test.basic.Sample018.main(Sample018.java:17)
Exception in thread “main”
標準出力には、期待通り4回目のルーチンで実行エラーが起こっていることが示されています。また、「at」から始まるメッセージは「スタックトレース」といいエラーの発生箇所から呼び出し元をたどる情報を提供します。(エラーが発生した行番号まで表示されます)
こういったエラーの恐れはいろいろなところに潜みますから、あらかじめ「でる恐れのあるところ」は予防線をはってエラー時の適切な処理を実装しておきたいですよね。
その方法論に行く前にもうひとつサンプルを実行してみましょう。Sample019を作ってみましょう。
package test.basic;
public class Sample019 {
public static void main(String[] args) {
throwEx();
}
private static void throwEx() throws NullPointerException{
NullPointerException npe = new NullPointerException();
throw npe;
}
}
この例では、throwEx()メソッドでNullPointerException(Null値を触ったときによく出るException)をインスタンス化しています。また、例外というのは「投げる」というのが正しい「動詞」のようで、
throw npe;
で呼び出し元に例外を「投げつけて」います。このようにExceptionも(特殊な役割を持っているとはいえ)クラスですので、プログラマが自作したり、また、故意にthrowすることが可能です。また、メソッドの宣言部に「throws NullPointerException」という句がありますが、これは、「このメソッドはNullPointerExceptionをなげるぜ」という宣言です。
さきほど、例外を「呼び出し元に投げつける」と書きましたが、投げつけられた方でも「なんらかのエラー処理をする」という選択と、「自分の呼び出し元に投げつける」という選択があります。以下では、この「なんらかのエラー処理をする」方法について、簡単に説明します。
このように例外が発生しそうな箇所(もしくは、発生させる箇所)で、Exceptionを補足するには、「try〜catch〜finally」句を仕掛けておきます。この構文は以下です。
try{
例外が発生するかもしれない処理
}catch(補足する例外クラス名1 変数名1){
クラス名1の例外が発生したときのエラー処理
}catch(補足する例外クラス名2 変数名2){
クラス名2の例外が発生したときのエラー処理
}finally{
例外の発生に関わらずかならず実行する処理。
}
それでは、まず、Sample018を修正してみましょう。Sample018をSample018_1という名前でコピーして、以下のように修正してください。
package test.basic;
public class Sample018_1 {
// int型の3の要素をもつ配列の宣言
static int[] i1 = new int[3];
public static void main(String[] args) {
for(int i=0;i<10;i++){
arrayAdd(i,i);
System.out.println(Integer.toString(i));
}
}
private static void arrayAdd(int i, int value){
try{
i1[i]=value;
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("配列があふれました。indexは"+Integer.toString(i));
}finally{
System.out.println("ここはfinally句です。");
}
}
}
arrayAdd()メソッドで配列あふれが発生していますので、このなかで例外の処理をしています。それではこれを実行してみてください。以下のような出力がでて、プログラムが止まることなく実行されていることが分かります(finally句をかならずとおっていることも分かりますね)。
ここはfinally句です。
0
ここはfinally句です。
1
ここはfinally句です。
2
配列があふれました。indexは3
ここはfinally句です。
3
配列があふれました。indexは4
ここはfinally句です。
4
配列があふれました。indexは5
ここはfinally句です。
5
配列があふれました。indexは6
ここはfinally句です。
6
配列があふれました。indexは7
ここはfinally句です。
7
配列があふれました。indexは8
ここはfinally句です。
8
配列があふれました。indexは9
ここはfinally句です。
9
次に、Sample019を修正してみましょう。Sample019をSample019_1という名前でコピーして、以下に修正してください。
package test.basic;
public class Sample019_1 {
public static void main(String[] args) {
try{
throwEx();
}catch(NullPointerException e){
System.out.println("NullPointerExceptionが発生しました。");
}finally{
System.out.println("ここはfinally句です。");
}
}
private static void throwEx() throws NullPointerException{
NullPointerException npe = new NullPointerException();
throw npe;
}
}
この例では、throwEx()メソッドは(その呼び出し元のmainメソッドへ)例外を放り投げることを決めているので、mainメソッドを修正しています。これを実行してみてください。プログラムは終了して、以下のようなメッセージがでましたか?
NullPointerExceptionが発生しました。
ここはfinally句です。
これで例外についての説明はとりあえず終わりです。
「例外を積極的に使うべきか否か」というのは議論が分かれるところで、極端な場合には(非常に軽微なエラーであっても)例外を発生させて上位プログラムに知らせるとかいうコーディングもありえます(エラーメッセージの代わりに使うようなイメージですね)。ですが、Exceptionを発生させることはコストの高い(負荷の高い)処理と言えます。例外をthrowすべきかどうかは、それが本当にベストな選択かどうか考えてきめるべき、というのが一般論です。
2007年02月26日(月) 15:35:21 Modified by wanderingse