例外処理

はじめに

ファイル I/Oに関しては、プログラム外とのやりとりになります。ということは、正しいプログラムであっても、引数でもらった名前のファイルが開けなかったりすると、当初の意図どおりは動けない訳です。

Java では、そのような処理は例外とし扱い、java.io.IOException などについては、例外の扱いの明確化を要求します。

つまり、

  • try-catch-finally ブロックを用いて、例外処理するか
  • メソッドに throws Exception 宣言をして、例外が起きることを明示するか

です。

例外処理は、こんな感じ。catch 節とfinally節のどちらか一方だけでも大丈夫です。

try {
    // 例外を起こすかもしれない処理
    // を含んだブロック
} catch (Exception1 e1) { // try ブロック内で Exception1 型の例外が起きたら、
                          // ここで捕まえる。対象の例外オブジェクトは e1 ね。
    // 例外処理内容1
} catch (Exception2 e2) { // try ブロック内で起きた例外が前のハンドラで捕まらず、
                          // ここまでもれてきたら、Exception2 型なら受け取るよ
    // 例外処理内容2
} finally {               // finally 節
   // 例外の有無に関わらず、実行して欲しい事柄を書く場所
}

これで、例外に応じた処理を書くことができます。 ちなみに、java.io.FileInputStream のコンストラクタで起きる java.io.FileNotFoundException は、

これらのクラスを catch 節に書いた場合も FileNotFoundException は catch できます。

catch-finally 節の利用法

catch 節に関する使い方のおすすめは以下のとおり。

  • 一般的な利用としては、Exception があった理由を表示して、終了する。あるいは、ユーザに次の処理を入力してもらって、次の動作を行う。
  • 絶対、Exception が起きないと確信していたら、絶対起きない旨書いておく。
} catch (Exception e) {
    throw new Error("This SHOULD NOT occure!"); // あるいは、 assert false;
}
  • デバッグ中なら、下記のようにバグ情報出力をおこなうとよいでしょう。
} catch (Exception e) {
    e.printStackTrace();
}
  • やってはいけないこと: 良くわからないからといって、見なかったことにする。先送りしても、別のバグと混ざって分かりにくくなるだけです。自分で自分にトラップ仕掛けるようなものです。絶対止めましょう。
} catch (Exception e) {
    /* みなかったことにしよう。。。 */
}
/* 事態は、さらに混迷を深める */

ちなみに、finally 節のよくある利用法は、

  • 後始末で行うような、ファイルを close() したり、ネットワーク処理で使った socket や port を閉じたり

というのが普通です。

Automatic Resource Management

ファイル I/O では、stream を開いて、一連の処理をした後で、例外の有無にかかわらず stream を閉じるってプログラムの形は多くなります。 このようなプログラムは、

try ( /* stream を開く */ ) {
  /* 一連の処理 */
}

という形で書くこともできます。

try (InputStream in = new FileInputStream(from); OutputStream out = new FileOutputStream(to)) {
    while (true) {
        /* in から read。IOException の可能性 */
        int size = in.read(buf, 0, BUFSIZE);
        if (size < 0) { /* EOF なら、loop を抜けて */
            break;
        } else {
            /* size 分, out に出力。IOException の可能性 */
            out.write(buf, 0, size);
        }
    }
}