例外の基本

例外処理の基本に関するメモ。

執筆時バージョン
Java

Java SE 8

予期せぬ事象に対処するためのもの

プログラムを実行していくうえで想定外の状況は避けて通れない。例えばDBからデータを取得するときにネットワークが不通になったりする場合など、それが故意か偶然かに関わらず発生するし巻き込まれてしまう。

そんなときプログラムはどうするべきだろうか。データが取得できないのにデータが取得できる前提のコードを実行していっても仕方ない。システム自体を中断(終了)させたり、データの取得をリトライするなどの対処用の処理を実行したいということになる。そんなことを可能にする仕組みが例外。

Javaはオブジェクト指向なので例外もオブジェクトとして扱うことになる。

例外が発生する場所

例外が発生する場所としては大別すると3種類。

  • 明示的に例外を発生させる

    • throw文が実行された

  • JVMが異常な状態を検知して例外を発生させる

    • ゼロ除算

    • プログラムのロード、リンクに関するエラー

    • メモリの使いすぎ、リソースの制限を超えたなど

  • 非同期例外が発生した

    • Thread.stop()が起動された(Thread.stop()は非推奨。Java SE 9で消滅予定)

    • 仮想マシン内部でエラーが発生

例外発生時の実行順序を把握する

通常の命令は順次実行されるので1行目が実行されたら2行目を実行することになりますが、例外が発生すると全く違う動きになります。

  1. 例外が発生した

  2. 次の行を実行せずにcatch節を探す

    1. 該当するcatch節がある場合

      1. 1度キャッチされたらそれ以降はまた通常の順次実行に戻る。

    2. 該当するcatch節がなかった場合

      1. 制御を呼び出し元に戻す。そこで「2.」へ戻りcatch節を探し続ける。このときmain()メソッドまで制御が戻っている場合はJVMの実行終了になる。

平たくと言うとcatch節を永遠と探し続けることになる。main()メソッドまでcatch節が見つからなければそのまま終了となってしまう。

main()メソッドに到達するまでにキャッチできればJVMの終了とはならないので、必ずしも例外が発生するその場でキャッチしなければいけないことはない。呼び出し元のどこかでキャッチするということでもよい。FWの設計として処理の入り口に近い部分でcatch節を書くことで、例外処理をまとめるといったやりかたも考えられる。

例外クラスの階層

これはしっかり把握しておかなければならない。例外クラスはこれらのいずれかを必ず継承している。どの例外クラスを継承しているかによって例外チェック時の扱いが変わる。

80%
Figure 1. 例外階層クラス図
例外クラス 概要

Throwable

すべての例外クラスの親クラス。Throwableを継承していないとthrowできないので例外クラスは必ずThrowableを継承している。

Error

通常のアプリケーションであればキャッチすべきではない重大な問題を現わす例外。

Exception

プログラムに関する例外クラス。Exceptionを継承するクラスを利用する場合は、必ず例外処理をする必要がある。(しないとコンパイルエラーとなる。)

RuntimeException

実行時例外。RuntimeExceptionを継承するクラスを利用する場合は非チェック例外なので、例外処理を必ずしもしなくてもよい。

チェック例外と非チェック例外

例外はどのクラスを継承しているかによって「チェック例外」「非チェック例外」に分類される。非チェック例外はRuntimeExceptionあるいはErrorのそのクラス自身かサブクラス。それ以外がチェック例外となる。「チェック例外」になるとコンパイラによって例外対処が行われているかのチェックが入るため、例外対処をしていないとコンパイルエラーとなる。

コンパイラによる例外(対処)のチェック

「チェック例外を投げる」あるいはチェック例外のthrowsが宣言されているメソッドを利用する場合は、コンパイラによって例外対処が行われているかのチェックが行われる。

コンパイルエラーにならないためには次のいずれかで対処する

  • try-catchをする

  • throwsでその例外を投げる主旨を宣言する

Appendix A: 参考

Appendix B: 改訂履歴

  • v1.0, 2017-05-23: 初稿