|
|
例外処理 |
H.Kamifuji . |
Javaではコンパイル時に発生するコンパイルエラーと実行時に発生する実行時エラーがあります。ここでは実行時に発生するエラーに対して別途記述した処理を行わせたい場合に使われるのが例外処理です。ここでは例外処理の仕組みと種類、そして例外処理の方法について確認していきます。 当ページでは、Linux CentOS7 の Gnome で動作テストしています。 現在(2021/08)では、JDK-16.0.2 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2021/11)では、JDK-17.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2023/04)では、JDK-20.0.1 にアップされています。一部、上位互換について、見直しを行っていきます。 現在(2024/10)では、JDK-23 にアップされています。一部、上位互換について、見直しを行っていきます。 |
|
ここでは例外とは何かについて簡単に確認しておきます。Javaでプログラムを作成した場合、コンパイルの時点でエラーが発生する場合と実行中にエラーが発生する場合があります。 コンパイルの時点でエラーが発生するのはプログラムが文法的に間違っている場合で、構文の使い方を間違えたとか変数名の記述ミスなどがあります。 例えば次のプログラムをコンパイルしてみます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample1_1{ public static void main(String args[]){ int n = 10; if (n == 10{ System.out.println("10です"); } } }コンパイルを行うと次のようにコンパイルエラーが発生します。 [xxxxxxxx@dddddddddd Exception]$ javac JSample1_1.java JSample1_1.java:5: エラー: ')'がありません if (n == 10{ ^ エラー1個 [xxxxxxxx@dddddddddd Exception]$ if文の条件式の後の「)」が記述されていないためです。このようにコンパイルの時点でエラーとなる場合は書き間違いなどの記述ミスやint型の変数にStringクラスのオブジェクトを代入しようとするなど文法上の間違いがあった場合となります。 続いて実行時のエラーです。実行時に発生するエラーは簡単に言えば予期せぬエラーです。例えばユーザーが入力した値を数値に変換して出力するプログラムであった場合、数値が入力されることを前提にプログラムが記述されているにも関わらず数値以外の文字が入力された時に実行時のエラーが発生します。 他にもファイルを開いて何か書き込みをしようとしたが、そのファイルが見つからなかった場合などにも実行時のエラーが発生します。ファイルが実際にあるかどうかはコンパイルの時点では判断することができません。ファイルを開こうとするプログラムの記述の方法が間違っていなければコンパイルは成功しプログラムは作成されますが、実際に実行してみたらファイルが見つからなくてエラーが発生するということです。 下記は実行時エラーとなるプログラムのサンプルです。 サンプルプログラム下記のサンプルを実行してみよう。class JSample1_2{ public static void main(String args[]){ int n[] = {18, 29, 36}; System.out.println("開始します"); for (int i = 0; i < 4; i++){ System.out.println(n[i]); } System.out.println("終了しました"); } }このプログラムはコンパイルは無事に成功します。 [xxxxxxxx@dddddddddd Exception]$ javac JSample1_2.java [xxxxxxxx@dddddddddd Exception]$ただプログラムを実際に実行してみると、途中でエラーが発生します。 [xxxxxxxx@dddddddddd Exception]$ java JSample1_2 開始します 18 29 36 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at JSample1_2.main(JSample1_2.java:8) [xxxxxxxx@dddddddddd Exception]$今回のプログラムは配列の要素が3つしかないのに4つ目の要素を参照しようとしたためにエラーが発生しています。Javaではこのようにプログラムの実行中にエラーが発生することを例外が発生したといいます。 先ほどのサンプルの実行結果を見て頂くと分かりますが例外が発生するとプログラムはそこで終了してしまいそれ以降のプログラムを実行しません。急にプログラムが終了してしまうと困ってしまう場合もありますし、プログラムを実行している人にエラーメッセージをそのまま表示したくない場合もあります。そこで例外が発生した時に単にプログラムを終了させるのではなく指定した動作をさせるための仕組みが用意されています。それが例外処理と呼ばれるものです。 例外が発生するのは様々な原因があります。例外処理では例外が起こった原因毎に異なる処理ができる仕組みが用意されています。次のページにて例外処理の基本的な記述方法を確認します。 |
例外処理を行うにはtry文を使用します。基本的な書式は次の通りです。 try{ 例外が発生しているかどうか調べる文1; 例外が発生しているかどうか調べる文2; ... } catch (例外クラス1 変数名1){ 例外クラス1の例外が発生した時に行う文; ... } catch (例外クラス2 変数名2){ 例外クラス2の例外が発生した時に行う文; ... }まずプログラムの中で例外が発生するかどうかを調べる対象の文をtryの後の「{」から「}」までのブロック内に記述します。そして例外が発生した時に行いたい処理をcatchの後の「{」から「}」までのブロック内に記述します。 処理の流れはtryのブロック内で例外が発生すると処理はいったん止まります。そして処理はcatchの箇所へ移ります。Javaでは例外の種類毎にクラスが用意されています。例外が発生すると対応するクラスのオブジェクトが作成され、そのオブジェクトがcatchへ渡されてきます。 渡されてきた例外クラスのオブジェクトがcatchの「(」から「)」の中に記述された例外クラスと一致した場合、変数に渡されてきた例外クラスのオブジェクトが代入され、そしてその後の「{」から「}」の中の処理が実行されることになります。catchは1つのtryに対して複数記述することができますので発生する可能性のある例外について複数のcatchを記述しておき別々の処理を行わせることが可能です。もし発生した例外に一致するcatchが無ければ何も行いません。 catchブロック内に記述された処理が実行されると、try文全体の処理が終了しtry文の次へ処理が移ります。 実際のプログラムにおける例外処理の流れ言葉で記述すると分かりにくいですが実際のプログラムを例にして確認していきます。次のプログラムを見てください。int n[] = {18, 29, 36}; System.out.println("開始します"); for (int i = 0; i < 4; i++){ System.out.println(n[i]); } System.out.println("終了しました");このプログラムを対象にして例外処理を記述してみます。まずは例外処理を行う対象の文をtryブロック内に入れる必要がありますが、例えばプログラム全体をtry文のブロック内に入れることもできます。 try{ int n[] = {18, 29, 36}; System.out.println("開始します"); for (int i = 0; i < 4; i++){ System.out.println(n[i]); } System.out.println("終了しました"); } catch(例外クラス 変数名){ }この場合、例外処理が終了してtry文の次へ処理が移るとプログラムは終わりなのでプログラムが終了します。 他の方法として例外が発生する可能性がある文だけをtry文の中に入れることもできます。 int n[] = {18, 29, 36}; System.out.println("開始します"); for (int i = 0; i < 4; i++){ try{ System.out.println(n[i]); } catch(例外クラス 変数名){ } } System.out.println("終了しました");tryブロック内にどの文を入れるのかは例外処理をどのように行うかによって検討する必要があります。今回は例外が発生したら配列要素の出力を終了し「終了しました」と画面に表示させるようにするため次のように記述することにします。 下記のサンプルを実行してみよう。 int n[] = {18, 29, 36}; System.out.println("開始します"); try{ for (int i = 0; i < 4; i++){ System.out.println(n[i]); } } catch(例外クラス 変数名){ } System.out.println("終了しました");次に例外が発生した時の処理を記述します。今回発生する可能性がある例外は「ArrayIndexOutOfBoundsException」という例外です。これは範囲を超えた配列の要素を参照した時に発生する例外です。そこで次のように記述します。 int n[] = {18, 29, 36}; System.out.println("開始します"); try{ for (int i = 0; i < 4; i++){ System.out.println(n[i]); } } catch(ArrayIndexOutOfBoundsException e){ System.out.println("配列の範囲を超えています"); } System.out.println("終了しました");tryブロック内で例外が発生した場合に、その例外がArrayIndexOutOfBoundsExceptionだとcatchブロック内の処理が実行されます。今回はエラーメッセージを画面に表示させているだけですが、渡されてきた例外クラスのオブジェクトを利用することもできます。これについては別のページで確認します。 例外処理を行うtry文の記述方法と基本的な使い方は以上となります。慣れないと複雑に思えるかもしれませんが何度か記述していると戸惑うことなく記述できるようになります。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample2_1{ public static void main(String args[]){ int n[] = {18, 29, 36}; System.out.println("開始します"); try{ for (int i = 0; i < 4; i++){ System.out.println(n[i]); } } catch(ArrayIndexOutOfBoundsException e){ System.out.println("配列の範囲を超えています"); } System.out.println("終了しました"); } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Exception]$ javac JSample2_1.java [xxxxxxxx@dddddddddd Exception]$ java JSample2_1 開始します 18 29 36 配列の範囲を超えています 終了しました [xxxxxxxx@dddddddddd Exception]$例外が発生してもエラーメッセージは表示されずcatchブロック内に記述した処理が実行されています。そしてプログラムも途中で終了することなくtry文以降の文も実行されていることが確認できます。 |
tryブロックの中で例外が発生すると残りの処理は行われずにcatchブロックへ処理が移ります。次の例を見てください。 try{ 実行する文1; 実行する文2; 実行する文3; } catch (例外クラス 変数名){ 例外が発生した時に実行する文; }tryブロックの中で例外が発生しなければ「実行する文1」「実行する文2」「実行する文3」と順に実行しtry文の次へ処理が移ります。それに対して例えば「実行する文1」「実行する文2」と実行してここで例外が発生した場合にひあ「実行する文3」は実行されずにcatchブロックへ処理が移ります。(そして例外の種類がcatchのところに書かれた例外だった場合にはcatchブロック内の処理が実行されます)。 このようにtryブロックの中の処理は実行されたりされなかったりする可能性があるのですけど、try文を終了する前に必ず実行させたい処理があった場合にはfinallyを使用して記述することが可能です。具体的には次のように記述します。 try{ 例外が発生しているかどうか調べる文1; 例外が発生しているかどうか調べる文2; ... } catch (例外クラス1 変数名1){ 例外クラス1の例外が発生した時に行う文; ... } catch (例外クラス2 変数名2){ 例外クラス2の例外が発生した時に行う文; ... } finally { 例外が発生するしないに関わらず実行する文; ... }finallyの後の「{」から「}」までのブロックに記述された処理は、try文の中で例外が発生してもしなくても必ず実行されます。その為、必ず行っておきたい処理がある場合にはfinallyブロックを用意してブロック内に記述するようにして下さい。 例えば次のように記述します。 int n[] = {18, 29, 36}; System.out.println("開始します"); try{ for (int i = 0; i < 4; i++){ System.out.println(n[i]); } } catch(ArrayIndexOutOfBoundsException e){ System.out.println("配列の範囲を超えています"); } finally{ System.out.println("配列の出力を終了しました"); } System.out.println("終了しました");この場合、try文の中で例外が発生してもしなくても必ず「配列の出力を終了しました」は画面に出力されることになります。 finallyとtry文の次に記述した場合との違いなお、必ず実行される処理ということであれば次のようにtry文の次に記述しても同じように見えます。int n[] = {18, 29, 36}; System.out.println("開始します"); try{ for (int i = 0; i < 4; i++){ System.out.println(n[i]); } } catch(ArrayIndexOutOfBoundsException e){ System.out.println("配列の範囲を超えています"); } System.out.println("配列の出力を終了しました"); System.out.println("終了しました");このプログラムの場合には同じ結果となりますが、もしもこのプログラムがメソッドの中で記述されていてcatchブロックの中でreturn文などにより処理がメソッドの呼び出し元へ戻ってしまう場合にはtry文の次に記述された処理は実行されません。finallyブロックを使った場合はatchブロックの中でreturn文などが実行された場合でも必ず実行されます。(ただしSystem.exit(0)によってプログラムが終了した場合を除きます)。 具体的には次のサンプルを参照して下さい。 では簡単な例で試しておきます。 サンプルプログラム下記のサンプルを実行してみよう。class JSample3_1{ public static void main(String args[]){ disp(1); disp(2); disp(3); } private static void disp(int no){ int n[] = {18, 29, 36}; try{ System.out.println(n[no]); } catch(ArrayIndexOutOfBoundsException e){ System.out.println("配列の範囲を超えています"); return; } finally{ System.out.println("要素の出力を終了します。"); } } }上記をコンパイルした後で実行すると次のように表示されます。 [xxxxxxxx@dddddddddd Exception]$ java JSample3_1 29 要素の出力を終了します。 36 要素の出力を終了します。 配列の範囲を超えています 要素の出力を終了します。 [xxxxxxxx@dddddddddd Exception]$finallyブロックに記述した処理は例外が発生してもしなくても必ず実行されます。またcatchブロック内でreturn文が実行されて呼び出し元へ処理が移る場合でもfinallyブロック内の処理は実行されています。 |
|