👌

Java言語の例外処理の基礎についての個人的まとめ

2022/07/13に公開

例外とは

  • 実行中のプログラムが停止して異常終了すること
  • 呼び出し先のメソッドでエラーが起こった時に使う

こんな時に「例外が発生した」と言う。

例えば、

int a = 3; int b = 3;
とかなら a/b = 1 として答えが出る(文法と実行時どちらも問題なし)が、
int a = 3; int b = 0;
とかだと文法上問題はない(int b = 0; 問題なし)が数学的には実行時に答えが出なくなる
(bで割れない→例外)。


例外処理とは

例外処理とは、コンパイル時に回避できないエラー(例外)を検知し、例外が発生した際の処理をあらかじめ記述するためのものです。
この例外処理を書いていない場合は、例外が発生した時に上の様なエラーになるため、プログラムが異常終了します。
例外処理を用いて、例外発生後の処理を記述していると、プログラムを異常終了させることなく動作させることが可能になります。

この例外の出る処理(上の例では、0で割る処理)を呼び出した、「呼び出し先のメソッド」に例外が出現したことを通知し、

その通知を受け取った「呼び出し元のメソッド」でエラー対策を実行する。

例外処理

例外処理のPointは

  • throw文:例外を投げる(呼び出しのメソッドに入る)

  • try文:例外が出る可能性のある処理を入れる(呼び出しのメソッドで作る)
    例外が発生したときは処理はcatchブロックに飛ぶのでtryブロックの中実行されない
    →例外が発生時、「してほしくない処理」もtryブロックの中に書くべき

  • catch文:例外に対応する処理(呼び出しのメソッドで作る)
    (finally(例外発生有無に関わらず必ず動作する処理)はまた後日)
    発生した例外が()内の例外型だった時のみcatchブロックの中の処理が実行される
    逆に
    発生した例外が()内の例外型じゃなった時は、tryブロックの中の処理が呼ばれる

try {
  例外が発生しうる処理(throw文とか);
} catch (例外クラス名(例外型ともいう) 変数名) {
  例外発生時の処理(例外型が一致した時のみ);
} finally {
  例外発生有無に関わらず必ず動作する処理;
}

catchで指定する例外クラス名は、tryで発生するであろう例外のクラス名(例外型)(ArithmeticException,NullPointerException...など)を指定。
変数名に関しては、「e」や「ex」を指定することが基本的に多い。

コード例

public class Ex1 {
  public static void main(String[] args) {
    System.out.println("プログラムの実行");
    
    try {
      System.out.println("NullPointerExceptionを意図的に発生");
      throw new NullPointerException("ぬるぽ発生");
      //例外がでる可能性のある処理
    } catch(NullPointerException e) {
      System.out.println(e.getMessage());
      System.out.println("NullPointerException発生");
      //例外が発生し、例外型がNullPointerExceptionならこのブロックを実行
    } catch(Exception e) {
      System.out.println(e.getMessage());
      System.out.println("Exception発生");
      //例外が発生し、例外型がExceptionならこのブロックを実行
    }
  }
}

また、

public static void main(String[] args) {
  try{
    func();//呼び出し元
  }
  catch(Exception e){
    e.printStackTrace();//funcメソッド内で例外が発生するとここに処理が移る
  }
}

上記の例で使用しているExceptionクラスはすべての例外に共通の親クラス(例外クラスの親子関係については以下)なので、どんな例外でもキャッチできる。

ただ、実際は発生しうる例外に合わせて処理を分ける方が望ましい。
(サンプルプログラムを参照)

例外の「型」について

例外では、Nullによる例外(値なし)、算術的な例外(0で割れない)、Index的な例外(配列の長さと繰り返し文)など、
さまざまな原因で例外が発生する。

その様々な「例外の型」を例外の種類ごとに開けてあるのが「例外クラス」で、そのクラスの継承関係には以下のような種類と関係がある

(throwableクラス以下が例外クラスで上でも述べた通り、Exception が主な例外の親クラスである)

①Errorクラス群[システムエラー]

  • Errorクラスとそのサブクラス群は、重大な問題、異常な状態、実行環境上のトラブルなどを表す。

  • Error群で見られるクラスとしては、VirtualMachineError(JVMが壊れているか、必要なリソースが足りなくなったか)、OutOfMemoryError(メモリ不足)など。

  • Errorクラス群は、プログラム上では対処の仕様がない回復不可能な例外に当たるため、例外処理の記述は不要

②Exceptionクラス群[チェック例外]

  • Exceptionクラスとそのサブクラス群(RuntimeExceptionクラスとそのサブクラス群は除く)は、プログラムを作っているうえで、例外の発生が予想でき対処の必要があるクラスを表す。

  • Exception群で見られるクラスとして、IOException(入出力処理の失敗)、TimeoutException(タイムアウトエラー)など。

  • Exceptionクラス群は、回復可能な例外になるので、例外処理を記述する必要がある。

  • 必要な例外処理が記述されていない場合は、コンパイルエラー

③RuntimeExceptionクラス群[非チェック例外/実行時例外]

  • RuntimeExceptionクラスとそのサブクラス群は、プログラム作成時に例外発生の想定はできるが、対処はしなくても良いクラスを表す。

  • RuntimeException群で見られるクラスとして、NullPointerException(変数にnullがある)、IndexOutOfBoundsException(配列・文字列などが範囲外)など。

  • NullPointerExceptionは、例えばすべてのメソッド呼び出しの際にチェックをしていては大変で、プログラムも見にくいものになってしまう。

  • そのため、RuntimeExceptionクラス群では、回復可能な例外ではあるが、例外処理の記述は必須ではない。(例外処理を記述することは可能)




[まとめ]

Error ‐エラー   ・・・例外処理を記述する必要はない。

Exception ‐チェック例外 ・・・例外処理を記述しないとコンパイルエラーが起こる。

RuntimeException ‐非チェック例外 ・・・例外処理の記述は任意。



以上です。間違えが見つかり次第、或いは知識がアップデートされ次第改訂していきます。🙏

参考記事
参考記事
参考記事

GitHubで編集を提案

Discussion