Java / 例外

Throwable からの継承の繋がり

結論: Java の例外はすべて Throwable のサブクラスで、その下が Error(回復を想定しない深刻な問題)と Exception(プログラムで対処しうる問題)に分かれます。Exception のうち RuntimeException とそのサブクラスは「非検査例外」、それ以外の Exception のサブクラスは「検査例外」です。検査例外は catch するか throws で宣言しないとコンパイルエラーになり、Error と RuntimeException 系はどちらも不要です。

継承の繋がり

  • Throwable
    • Error(非検査)
      • VirtualMachineError
        • OutOfMemoryError
        • StackOverflowError
    • Exception
      • RuntimeException(非検査)
        • NullPointerException
        • IllegalArgumentException
        • ArithmeticException
        • IndexOutOfBoundsException
        • ClassCastException
      • 検査例外(RuntimeException 以外の Exception のサブクラス)
        • IOException
        • SQLException
        • ReflectiveOperationException
          • ClassNotFoundException

途中の親クラスは代表的なものだけ載せています。

上の「検査例外」は分類名で、そういう名前のクラスがあるわけではありません。IOException などは Exception の(直接または間接の)サブクラスです。なお Throwable 自身と Exception 自身も検査例外として扱われます。

検査例外と非検査例外の違い

種類 該当するクラス catch か throws が必須か 典型的な原因
検査例外 Exception のサブクラスのうち RuntimeException 系以外 必須(ないとコンパイルエラー) ファイル・DB・ネットワークなど外部要因
非検査例外 RuntimeException とそのサブクラス 不要 null 参照、範囲外の添字など、プログラムの誤り
エラー(非検査) Error とそのサブクラス 不要 メモリ不足、スタックの使い切りなど JVM 側の問題

コード例

// 検査例外: catch するか throws で宣言しないとコンパイルエラー
String readFirstLine(Path path) throws IOException {
    return Files.readAllLines(path).get(0);
}

// 非検査例外: 宣言しなくてもコンパイルは通る(実行時に発生しうる)
int divide(int a, int b) {
    return a / b;   // b が 0 なら ArithmeticException
}

原理(なぜそうなるか)

検査例外は、プログラムが正しくても外部の状況(ファイルがない、接続が切れた等)によって起こりうる失敗です。呼び出し側が対処を考える必要があるため、Java はコンパイル時に catch か throws のどちらかを書くことを強制します。

RuntimeException 系は、null チェックの漏れや添字の範囲外など、コードを直せば防げる誤りです。これらはほぼどの行でも起こりうるので、すべてに宣言を強制すると実用になりません。そのため宣言は任意です。Error は JVM の資源不足など、アプリケーションが通常は回復できない状況を表すため、こちらも宣言を求めません。

catch (Exception e) は RuntimeException 系も含めて捕まえます。catch は上から順に照合されるので、サブクラスの catch を親クラスの catch より先に書きます(逆に書くと、サブクラスは捕捉済みとしてコンパイルエラーになります)。

(この初版はAIが作成。随時書き換え)

索引へ戻る