これまでの記事では、変数・条件分岐・繰り返し・配列・メソッド・クラスなど、Javaのさまざまな文法を学習してきました。
しかし、プログラムを実行していると、予想していなかった問題が発生することがあります。
例えば、次のようなケースです。
このような問題が発生したときに、プログラムでどのように対応するかを考える仕組みが例外処理です。
例外とは、プログラムの実行中に発生する、通常の処理の流れを変える問題を表す仕組みです。
例えば、次のコードを見てみましょう。
int a = 10;
int b = 0;
System.out.println(a / b);
整数の割り算では、0で割ることはできません。
そのため、このコードを実行すると、ArithmeticExceptionという例外が発生します。
例外を処理していない場合、通常はその後の処理が実行されず、プログラムが異常終了する原因になります。
プログラムのエラーには、いくつかの種類があります。
Javaの文法に誤りがある場合など、コンパイル時に問題が見つかると、プログラムを実行できません。
int number = ;
このコードは、変数に代入する値が書かれていないため、正しいJavaの文法ではありません。
コンパイルはできても、実行中に問題が発生することがあります。
int number = 0;
int result = 10 / number;
この例では、コンパイル後に実行して初めて、0で割ろうとしたことによる例外が発生します。
コンパイルエラーはプログラムを実行する前に見つかる問題、例外は実行中などに発生する問題と考えると分かりやすいでしょう。
Javaでは、例外が発生する可能性のある処理をtryブロックに記述し、発生した例外に対応する処理をcatchブロックに記述できます。
基本構文は次のとおりです。
try {
// 例外が発生する可能性のある処理
} catch (例外の型 変数名) {
// 例外が発生した場合の処理
}
try・catchを使うことで、例外が発生した場合の対応をプログラムに記述できます。
先ほどの0で割るプログラムを、try・catchで囲んでみます。
public class Main {
public static void main(String[] args) {
try {
int a = 10;
int b = 0;
System.out.println(a / b);
} catch (ArithmeticException e) {
System.out.println("0で割ることはできません。");
}
System.out.println("プログラムを終了します。");
}
}
0で割ることはできません。
プログラムを終了します。
tryブロックの中でArithmeticExceptionが発生すると、その時点でtryブロックの残りの処理は中断され、対応するcatchブロックへ移ります。
catchブロックの処理が終わると、その後の処理が続けて実行されます。
今回の例では、最後の「プログラムを終了します。」も表示されます。
今回のプログラムの動きを整理してみましょう。
tryブロックの処理を開始
↓
10 / 0 を実行
↓
ArithmeticExceptionが発生
↓
対応するcatchブロックへ移動
↓
エラーメッセージを表示
↓
catchブロックの後の処理を実行
例外が発生すると、通常の処理を中断して、対応するcatchブロックへ移るという点が重要です。
次の部分を見てみましょう。
catch (ArithmeticException e) {
System.out.println("0で割ることはできません。");
}
ここでは、ArithmeticException型の例外を変数eで受け取っています。
変数名はe以外でも構いません。例えば、次のようにも書けます。
catch (ArithmeticException exception) {
System.out.println("計算中に問題が発生しました。");
}
例外の情報を確認したい場合は、次のように書くこともできます。
catch (ArithmeticException e) {
System.out.println(e.getMessage());
}
getMessage()では、例外に関連付けられたメッセージを取得できます。ただし、メッセージの内容は例外の種類や実行環境などによって異なる場合があります。
プログラムによっては、複数の種類の例外に対応する必要があります。
その場合は、catchブロックを複数記述できます。
public class Main {
public static void main(String[] args) {
try {
int[] numbers = {10, 20, 30};
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("配列の範囲外が指定されました。");
} catch (ArithmeticException e) {
System.out.println("計算中に問題が発生しました。");
}
}
}
配列の範囲外が指定されました。
今回のコードでは、要素数が3の配列に対してnumbers[5]を指定しています。
配列の有効な添字は0・1・2なので、ArrayIndexOutOfBoundsExceptionが発生します。
そのため、最初のcatchブロックが実行されます。
発生した例外に対応するcatchブロックが実行されることを覚えておきましょう。
catchでは、捕捉する例外の型を指定します。
そのため、発生した例外に対応するcatchブロックがない場合、その例外はこのtry・catchでは処理されません。
例えば、次のコードではArithmeticExceptionだけを捕捉しています。
try {
// 処理
} catch (ArithmeticException e) {
// 計算に関する例外への対応
}
このcatchブロックは、すべての例外を無条件に処理するものではありません。
どのような例外が発生する可能性があるのかを考え、必要な例外に対応することが大切です。
例外処理を利用すると、問題が発生したときの対応を明確にできます。
ただし、例外をcatchすれば、どのような問題でも安全に解決できるわけではありません。
例外が発生した原因を確認し、適切な対応を行うことが重要です。
今回紹介した基本的なtry・catchでは、tryブロックとcatchブロックを組み合わせて記述します。
try {
// 処理
} catch (ArithmeticException e) {
// 対応する処理
}
発生した例外に対応していない型を指定すると、そのcatchブロックでは例外を処理できません。
例外の種類に合った型を指定することが大切です。
tryブロック内で例外が発生すると、通常、その後の処理は中断され、対応するcatchブロックへ移ります。
例外を捕捉することと、問題の原因を解決することは別です。
必要に応じて、入力値の確認や処理方法の見直しなども行いましょう。
例外処理は、予想外の問題が発生した場合に備えるための重要な仕組みです。
まずは、tryで処理を囲み、catchで例外に対応する基本的な書き方を身につけましょう。
次回は、例外処理の続きとして、finally・throws・例外を呼び出し元へ伝える仕組みなどを学習します。