Skip to content

Java講座: 例外処理

これからみなさんが開発をするようになると、「プログラムは正しいはずなのになぜかエラーが発生する」というような状況が起こることがあるかもしれません。
そのような場合に、エラーの内容をログとして保存したり、安全にプログラムを終了させたりできるようにする仕組みを例外処理といいます。

例外とは?

例外とはプログラムを実行する際に発生するエラーのことです。JavaではこれをExceptionとして扱うことができます。例えばこんな例外があります。

  • 存在しないファイルを開こうとした(FileNotFoundException)
  • 0除算をしようとした(ArithmeticException)
  • 中身がnullの変数にアクセスしようとした(NullPointerException)

例外処理をする理由

例外処理をしなかった場合、エラーが発生した瞬間にプログラムが強制終了してしまいます。しかし、例外処理によってこの挙動はある程度制御することができます。 たとえば、エラーが発生しても安全に次の処理へ進めたり、わかりやすいエラーメッセージを表示したり、エラーの原因をログに記録して修正しやすくしたりできます。

例外処理の基本構文(try-catch-finally)

例外処理の8割はこの構文で処理できるといっても過言です。基本はtrycatchfinallyの3つのブロックを使って処理します。 tryブロック内にエラーが発生する可能性があるコードを、catchブロック内にエラー発生時の処理を、finallyブロック内にはエラーの有無に関わらず最後に必ず実行したい処理を記述します。また、finallyブロックは省略可能です。

例外はこの構文で例外処理を行わないとコンパイルエラーとなり、そもそもプログラムを実行することができません。

サンプルコード

例外処理の例として、「数値が書かれたテキストファイル(input.txt)を読み込み、テキストを数値(int)に変換して出力する」プログラムを添付します。ファイルの読み込みに使用するBufferedReaderIOExceptionを、FileReaderFileNotFoundExceptionを引き起こす可能性があるため、try-catch構文でキャッチします。

java
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        String path = "input.txt";

        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            String line = "";

            while ((line = br.readLine()) != null) {
                int lineInt = Integer.parseInt(line);
                System.out.println(lineInt);
            }
        } catch (FileNotFoundException e) {
            System.err.println("エラー: " + path + "が存在しません");
            System.out.println(e.getMessage());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

同じディレクトリにinput.txtがない状態でこのプログラムを実行してみてください。おそらく

txt
エラー: input.txtが存在しません
input.txt (No such file or directory)

みたいなメッセージが出力されるはずです。

では、input.txtに以下のように書いてみたらどうでしょう?

txt
10
20
30
ABC

実行してみるとおそらく途中でエラーが発生すると思います。

txt
Exception in thread "main" java.lang.NumberFormatException: For input string: "ABC"
        at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
        at java.base/java.lang.Integer.parseInt(Integer.java:565)
        at java.base/java.lang.Integer.parseInt(Integer.java:662)
        at Main.main(Main.java:14)

ここで「あれ?」と思った人はセンスがあります。

重要なのはエラーメッセージにあるjava.lang.NumberFormatExceptionという文章です。 さきほど「例外はtry-catch構文でキャッチしないとコンパイルエラーとなる」と説明しましたが、例外であるはずのNumberFormatExceptionは今回のコードではキャッチしていません。それなのに普通にコンパイルできてしまいました。これにはちゃんとした理由があります。

例外の種類

Javaのエラー・例外には大きく分けて3つの種類があります。

1つ目は検査例外(Exception)です。IOExceptionSQLExceptionなどが当てはまり、try-catch構文で例外処理を行わないとコンパイルエラーとなります。

2つ目は**非検査例外(RuntimeException)**です。検査例外と異なり、例外処理を行う必要はありません。NumberFormatExceptionIllegalArgumentExceptionNullPointerExceptionなどが当てはまります。先程NumberFormatExceptionをキャッチしていないのにコンパイルができたのはそれが理由だったわけですね。

3つ目は**エラー(Error)**です。これは「もうプログラムではどうしようもない致命的な事態」が発生したときに起こり、StackOverflowErrorOutOfMemoryErrorなどが当てはまります。これはもうどうしようもないものなので例外処理を行う必要はありません。