Java例外処理(Exception)
例外処理(Exception)
例外(Exception)とは、プログラムの実行中に発生する異常な状況のことをいう。
プログラミングにおけるエラー(Error)には2種類がある。コンパイル(Compile)時に発生するエラーと、ランタイム(Runtime)時に発生するエラーである。コンパイル時に発生するエラーは、文法的な誤りや文脈に合わないコードを書いたことで発生するため、プログラム自体が実行すらできない状態である。それに対してランタイム時に発生するエラーは、プログラムの実行中に予期しない状況で発生するため、誤動作したりプログラムが終了したりする現象が起こる。
例えば、オブジェクトを宣言しただけで生成する前に参照した場合、整数を0で割った場合、ファイルにアクセスしようとしたが実際のファイルがない場合、配列のサイズを超えたインデックスにアクセスした場合など、多くの例外状況が存在する。
Javaは、このようなランタイム時に発生するエラーを例外(Exception)と呼び、例外状況を処理できるようにしている。
例外関連クラス
- java.lang.Object
- java.lang.Throwable
- java.lang.Error
- java.lang.Exception
- java.lang.RuntimeException
- Unchecked Exception
- Other Exception
- Checked Exception
- java.lang.Throwable
例外(Exception)も結局はクラスである。そのため、すべての例外(Exception)クラスはThrowableクラスを継承しており、Throwableは最上位クラスObjectの子クラスである。
Throwableを継承するクラスにはErrorとExceptionがある。Errorは主にハードウェア関連の例外を処理するクラスであり、システムレベルの重大なエラーである。例えば、メモリ問題(OutOfMemoryError)、スタックオーバーフロー、スレッド割り込みが発生した場合などのエラーがある。この種類の例外は、コードで処理するよりも、システムに変更を加えて問題を処理する場合が一般的である。
Exceptionは大きく、RuntimeExceptionを継承した例外と、継承していない例外に分けられる。RuntimeExceptionを基準に分ける理由は、例外指定を行うかどうかによる。RuntimeExceptionに関連する例外は、別途指定しなくても大きな問題にならないことが多い。例外を指定するために必要な労力が、例外を指定して得られる効果より大きいためである。このような例外処理はJVMに任せるほうが効率的である。
RuntimeExceptionに関連する例外も、Errorと同じように一般的には別途指定しない場合が多い。一つひとつにかかる労力が、例外を指定して得られる効率よりも大きいためである。
Checked Exception、Unchecked Exception
- Checked Exception
- 必ず例外処理をしなければならない。
- トランザクションRollbackは行われない。
- RuntimeExceptionを継承しない。(X)
- IOException, SQLException
- Unchecked Exception
- 例外処理をしなくてもよい。
- トランザクションRollbackが行われる。
- RuntimeExceptionを継承する。(O)
- NullPointerException, IllegalArgumentException
基本的な例外処理 - try~catch~finally
例外処理をメソッド内で直接行うには、try、catch、finallyキーワードを使用する。
try {
// tryブロック
} catch (例外型1 引数1) {
// try例外処理ブロック1
} catch (例外型2 引数2) {
// try例外処理ブロック2
}
...
} catch (例外型N 引数N) {
// try例外処理ブロックN
} finally {
// finallyブロック
}
tryブロックは、例外が発生する可能性のある範囲を指定するブロックである。プログラム実行中に例外が発生し得る範囲をtryブロックとして指定すればよい。tryブロックには少なくとも1つ以上のcatchブロックが必要である。
catchブロックは、tryブロックで例外が発生したときに実行されるブロックである。catchブロックの例外型の引数には、tryブロックで発生した例外に関する情報が含まれている。tryブロックではさまざまな例外が発生する可能性があり、その例外の種類に応じて複数のcatchブロックを指定できる。
finallyブロックは任意であり、例外の発生有無に関係なく必ず実行されるブロックである。つまり、tryブロックが実行されると、例外が発生しても発生しなくても必ず実行されるブロックである。
通常、オブジェクトを生成し、必ずcloseしなければならない場合によく使用される。例えば、ファイルを開いて閉じる場合や、データベースを開いて必ず閉じる必要がある場合などがある。
package com.devkuma.tutorial.exception;
public class TryCatch {
public static void main(String[] args) {
try {
int i = Integer.parseInt(args[0]);
System.out.println("i=" + i);
} catch (NumberFormatException e) {
System.out.println("NumberFormatException");
throw e;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException");
throw e;
}
}
}
改良されたtry~catchでの複数例外
JDK 1.7の拡張機能により、try~catchで複数の例外を受け取って処理できるようになった。 互いに異なる継承階層にある例外同士で可能である。例えば、NumberFormatExceptionとArrayIndexOutOfBoundsExceptionは同時に処理できる。
JDK 1.7以前は、以下のような方法で例外を処理していた。
...省略...
} catch (NumberFormatException e) {
System.out.println("NumberFormatException");
throw e;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException");
throw e;
}
JDK 1.7以降は、以下のような方法が可能になった。
...省略...
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
System.out.println("Exception");
throw e;
}
例外処理を渡す - throws
例外を処理する方法には、try~catch文でメソッド内で処理する方法と、発生した例外を呼び出し元のメソッドへ渡す方法がある。
このような例外処理方式は、1つのメソッドですべて処理する場合に便利であり、throwsキーワードを使用して例外を指定する。処理する例外が多い場合は、カンマ(,)で区切って列挙する。
public void method() throws 例外クラス[, 例外クラス...]
人為的な例外発生 - throw
JVMはJavaプログラムの実行中に例外が発生すると、自動的に該当する例外オブジェクトを発生させ、その後、例外を処理するためのcatchルーチンを探す。
例外はJVMによって実行中に発生することもあるが、ユーザープログラムで人為的に例外を発生させることもできる。Javaは例外を人為的に発生させるためにthrowキーワードを使用する。
throw 例外オブジェクト;
または
throw new 例外クラス(引数);
ユーザー定義例外クラス
ユーザーは、Javaで提供される例外クラス以外に、追加で自分自身のクラスを作成することもできる。ユーザー定義例外クラスを作るには、Exceptionクラスを継承して新しい例外クラスを作成すればよい。