AxiZ沖縄ブログ

エンジニア育成学校【AxiZ沖縄】のブログです。

Java応用の補足【例外】

チェック例外と非チェック例外の違い

チェック例外と非チェック例外では何が違うのか、
についての補足説明です。

  • 非チェック例外

    非チェック例外とは、RuntimeExceptionを継承した例外クラスのことで、
    非チェック例外は発生する可能性があったとしても、
    対処をする必要はありません。
    対処しなくてもコンパイルエラーが発生しないため、
    コンパイラがチェックしない、つまり「非チェック」の例外となります。

  • チェック例外

    RuntimeExcetionとそのサブクラス以外の
    Exceptionクラスを継承しているクラスを指します。
    チェック例外に関しては発生する可能性がある場合、
    try~catch、または throws 宣言を用いて対処することが求められます。
    対処していない場合はコンパイルエラーになります。
    対処しているかどうかをコンパイラが「チェック」する例外となります。

仕様的に説明するとこんな感じなのですが、
この違いをちゃんと理解するためには、
具体的に何を基準に分けているかを知らないと理解が深まりません。

簡単に言えば、非チェック例外は、プログラムのバグです。
プログラムの書き方を見直せば、大抵の場合解決できます。
一方でチェック例外とは、
Javaのプログラム外とやり取りする場合に発生する可能性がある例外で、
プログラムをどれだけ正しく書いても発生する可能性のあるものになります。

具体例で見ていきましょう。
まず、非チェック例外から考えてみます。

public static void test(int n) {
    System.out.println(10 / n);
}

このようなプログラムの場合、
引数で0が渡されたら例外が発生します。
これを次のように書き換えてみます。

public static void test(int n) {
    if (n != 0) {
        System.out.println(10 / n);
    }
}

こうすれば、例外が発生する可能性はなくなります。
このように、プログラムを見直せば防げるものが非チェック例外です。
だからコンパイラもチェックをしないわけです。

次はチェック例外です。
次のようなソースコードがあったとします。

public static void main(String[] args) {
    File file = new File("C:\\test\\test.java");
    file.createNewFile();
}

このソースは、例外の対処をしないとコンパイルエラーになります。
このプログラムは、ファイルを作成するプログラムで、
プログラム自体が何か悪いわけではありません。
ただ、もしこのプログラムを実行するPCに「test」というフォルダがなければ作成することができません。
フォルダが存在しても、セキュリティの問題でアクセスできない可能性もあります。
このプログラムは、環境によってうまくいくことも失敗することもあるわけです。

要はこのプログラムのように、
プログラムのソースコード自体は悪い部分はないけれど、
環境や状況によってはプログラムに関係なく処理が失敗する可能性があるときに発生するのがチェック例外です。

シチュエーションとしては

  • ファイルの読み書き
  • ネットワーク関連の処理
  • データベース関連の処理

等です。
ネットワーク関連は、
そもそもネットワークがつながっていなければ、
プログラムにどれだけスキがなくても失敗します。
データベース関連は、
データベースのサービスが起動していなければ失敗します。
このように、処理が失敗するか成功するかが環境に依存するかどうか。
それがチェック例外かどうかの基準になります。

非チェック例外の扱い

以下のようなソースコードがあったとします。

public static int div(int a, int b) {
    return a / b;
}

これは与えられた引数から割り算の商を返すメソッドです。
この処理内容だと、bが0だった場合、例外が発生します。
このままで放置してしまうと、プログラムが強制終了しますので、
何かしらの対処が必要になります。
ここで、対処方法が2通り考えられます。

// パターン1
public static int div(int a, int b) {
    if (b == 0) {
        return 0;
    }
    return a / b;
}

// パターン2
public static int div(int a, int b) {
    int n = 0;
    try {
        n = a / b;
    } catch (ArithmeticException e) {
        return 0;
    }
    return n;
}

パターン1の場合は、if文を使って例外が発生しないように処理します。
パターン2の場合は、try~catchを使って、
例外が発生した場合の処理を書いて対処します。
どちらの方が良いのか。
絶対的な答えはありませんが、
基本的にはパターン1の方が良いです。
理由は、例外が発生した場合の処理のコストというのは、
ifによる条件分岐よりも処理のコストが大きいからです。

非チェック例外は、他にもNullPointerExceptionなど、
色々な種類がありますが、 基本的にはif文などの条件分岐を駆使すれば例外の発生自体をなくすことができます。
非チェック例外の場合は、基本的にtry~catchを使用せずに対処するようにしましょう。