Java 例外処理
はじめに
実行時に想定外の事態が起きた場合に対処できるように「例外」についてご紹介します。
例外処理(1)
1.例外処理の必要性
プログラムを設計する上で、バグはあってはならないことです。 社会インフラを支える大規模システムにバグが生じると、大きな社会問題や損害賠償につながる可能性があります。
- 文法エラー(コンパイルエラー) 文法間違いにより起こります。
- 実行時エラー(例外) プログラム実行中に発生した異常事態により起こります。
- 論理エラー 実行は問題なくできるが、期待した結果が得られない状況です。
《Javaにおけるエラーの種類》
コンパイルで失敗します。
例) セミコロン「;」忘れ、全角スペースの混在、変数名間違い 等
動作の継続ができなくなります。
例) 0で割る、存在しないファイルのオープン 等
例) 式内の「+」を「-」と間違えて記述する 等
Javaでは、実行時に発生したエラーを「例外」と呼びます。
例外が発生しても例外処理を記述していないと、プログラムは強制終了されます。
稼働し続けるシステムは、「例外」によって強制終了することを未然に防ぐ必要があるため、「例外処理」が必要になります。
例外処理で例外が起きた場合の対処法を記述しておくことで、、プログラムは強制終了されることなく、実行を継続できます。
2.例外の種類
Javaで扱われる例外は、「checked 例外」と「unchecked 例外」の2種類に分類されます。
更に、Java APIで提供されている例外クラスは、3種類に分かれます。
《例外クラス》
例外クラス | 例外の種類 | 例外内容 | 例外処理 |
Errorクラス | unchecked 例外 | 致命的で対応しようがないエラー 例) ハードウェアの故障、メモリ不足 等 |
任意 |
RuntimeExceptionクラス | unchecked 例外 | 必ずしも発生の想定が必要でないエラー 例) 変数がnull、配列のインデックス外アクセス 等 |
任意 |
Exceptionクラス | checked 例外 | 発生を想定して対処しなければいけないエラー 例) ファイル読み書き、ネットワーク接続 等 |
必須 |
これらの例外クラスは、次のような継承関係にあります。
Errorクラス、RuntimeExceptionクラス、Exceptionクラスをスーパークラスに持つ例外クラス(サブクラス)をいくつかご紹介します。
スーパークラス | サブクラス名 | 例外処理 | 説明 |
Errorクラス | VirtualMachineError | 任意 | JVMが故障または、 動作を継続するのに必要なリソースが不足した場合に発生 |
RuntimeExceptionクラス | ArithmeticException | 任意 | 整数をゼロで除算した場合に発生 |
RuntimeExceptionクラス | NullPointerException | 任意 | オブジェクトが必要な場合に、 nullに対して操作を試みた場合に発生 |
RuntimeExceptionクラス | IndexOutOfBoundsException | 任意 | ある種のインデックスを範囲外で使用した場合に発生 |
Exceptionクラス | IOException | 必須 | 入出力(I/O)関連で何かしらの例外が発生した場合に発生 |
Exceptionクラス | SQLException | 必須 | データベース・アクセス時にエラーがあった場合に発生 |
Exceptionクラス | ParseException | 必須 | 解析中に予想外のエラーがあった場合に発生 |
Exceptionクラス | TimeoutException | 必須 | 処理の結果待ち状態(ブロック操作)が、 タイムアウトによって失敗した場合に発生 |
例外処理(2)
Javaにおける例外処理方法を2種類ご紹介します。
- try-catch-finallyブロックよる例外処理
- throwsキーワードによる例外処理
《例外処理の方法》
1.try-catch-finallyブロック
try-catch-finallyブロックは、tryブロック、catchブロック、finallyブロックで構成されます。
tryブロック
例外が発生しそうな箇所をtryブロックで囲みます。
catchブロック
例外発生時の処理をcatchブロック内に記述します。
finallyブロック
例外発生に関わらず必ず実行したい処理をfinallyブロックに記述します。
例外が発生しそうな処理
} catch(例外クラス 変数名){
例外発生時の処理
} finally{
例外発生に関わらず必ず実行したい処理(データベースやファイルのクローズ処理 等)
}
尚、try-catch-finallyブロックは、全てのブロックを記述する必要はありません。
以下の組み合わせが可能です。
- try-catch
- try-finally
- try-catch-finally
try-catch-finallyを使用したサンプルコードです。
public static void main(String[] args) {
int[] numArray = {10, 20, 30, 40};
for(int i = 0 ; i < 5; i++) {
try {
System.out.print("numArray:" + numArray[i]);
System.out.println("、" + (i + 1) + "回目ループ");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println((i + 1) + "回目のループで例外が発生");
} finally {
System.out.println("finallyの実行" + "¥n");
}
}
System.out.println("End");
}
}
finallyの実行
numArray:20、2回目ループ
finallyの実行
numArray:30、3回目ループ
finallyの実行
numArray:40、4回目ループ
finallyの実行
5回目のループで例外が発生
finallyの実行
End
2種類以上の例外は、catchブロックを複数記述することで対応できます。
次のサンプルコードは、catchブロックを複数記述することで複数の例外に対応させています。
public static void main(String[] args) {
int[] numArray = {0, 10, 20, 30, 50};
int ans;
for(int i = 0 ; i < 5; i++) {
try {
ans = numArray[i + 1] % numArray[i];
System.out.print("余り:" + ans);
System.out.println("、" + (i + 1) + "回目ループ");
} catch (ArithmeticException e) { //1つ目のcatchブロック
System.out.println((i + 1) + "回目のループで例外が発生");
System.out.println("ゼロで割ることはできません。");
} catch (ArrayIndexOutOfBoundsException e) { //2つ目のcatchブロック
System.out.println((i + 1) + "回目のループで例外が発生");
System.out.println("インデックスを範囲外で使用しています。");
} finally {
System.out.println("finallyの実行" + "¥n");
}
}
System.out.println("End");
}
}
ゼロで割ることはできません。
finallyの実行
余り:0、2回目ループ
finallyの実行
余り:10、3回目ループ
finallyの実行
余り:20、4回目ループ
finallyの実行
5回目のループで例外が発生
インデックスを範囲外で使用しています。
finallyの実行
End
2.throwsキーワード
throwsキーワードは、例外が発生する可能性のあるメソッドを定義するときに指定します。
「throws 例外クラス名」は、例外クラスがunchecked 例外の場合は省略可能です。 一方で、checked 例外で省略する場合は、メソッド内でtry-catch-finallyブロックを用いて例外処理を行う必要があります。
throwsキーワードを使う上で、一つ注意点があります。
それは、"throwsキーワードが記述されているメソッド側は"、例外処理をしなくてよいということです。つまり、"メソッドを呼び出す側"が、try-catch-finallyブロックを用いて例外処理をしなければならないということです。
- try-catch-finallyブロックよる例外処理 例外処理を自分で行う
- throwsキーワードによる例外処理 例外処理を呼び出し元に委ねる
まとめ
「例外処理」についてご紹介しました。 お役に立てると幸いです。