連載
» 2011年11月01日 00時00分 公開

プログラマの宿命! 例外とエラー処理を理解する【改訂版】Eclipseではじめるプログラミング(23)(2/3 ページ)

[小山博史,株式会社ガリレオ]

コンパイル時にチェックされる例外

 例外の基本は「コンパイル時にチェックされる例外」にあります。この例外を使えば、例外が発生した後にアプリケーションが引き続き処理を続行できるようになります。

 例えばWebブラウザを使って、あるURLへアクセスしたところ、ネットワークの問題でアクセスできなかった場合に、Webブラウザが停止してしまって再起動が必要となるのでは使い勝手が悪いはずです。そのURLへのアクセスをあきらめて、ほかのURLへアクセスできるように、Webブラウザは停止せずに引き続き処理を続行するようにプログラムを作成することになるでしょう。

 こういった場合に「コンパイル時にチェックされる例外」は役に立ちます。Javaでは、この例外をjava.lang.Exceptionクラスとして表現しています。このクラスのサブクラスとしては、入出力処理における例外を表す「java.io.IOExcepiton」や、データベース処理における例外を表す「java.sql.SQLException」があります。

Eclipseで例外処理を追加するのは、ワンクリック

 それでは、Eclipseを起動して「コンパイル時にチェックされる例外」を使用するプログラムを作成してみましょう。mainメソッドを持つ次のSample01クラスを新規に作成してください。

package sample23;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Sample01 {
    //----- ここから
    public static void main(String[] args) {
        BufferedReader in = new BufferedReader(
                            new InputStreamReader(System.in));
        System.out.print("Input name:");
        String name = in.readLine();
        in.close();
        System.out.println("Hello, "+name);
    }
    //----- ここまで
}

 さて、このようにコーディングすると、Eclipseでは「in.readLine()」と「in.close()」に赤い下線が引かれて、エラーがあることが分かります。これは、これらのメソッドを呼び出すと中でエラーが発生する可能性があるので、例外を捕捉(キャッチ)するコードを追加する必要があることを意味しています。例外を捕捉するコードを記述するには「try」文を使います。

 細かい説明をする前に、Eclipseの機能を使ってtry文を追加してみましょう。エディタに表示されている「Sample310.java」の「in.readLine()」の行で左側に表示されているバツ印付きのランプをマウスの左ボタンでクリックします。すると、修正方法を選択するポップアップメニューが表示されますから、そこから[try/catchで囲む]を選んでマウスで左クリックしてください。自動的に「in.readLine();」を囲むようにtry文が追加されます。

 また、java.io.IOExceptionという例外の型がimportされます。

 「in.close()」の行でもjava.io.IOExceptionの例外が発生する可能性があることからエラーとなっています。この処理は後述するように特別なので、「finally」節というものを次のように追加してその中へ入れます。

 また、「in.readLine()」の行で行ったのと同様にして例外をキャッチするコードも追加します。「System.out.println("Hello, "+ name)」の行については、nameの初期化がされていないことが原因でエラーが出ていますので、これもtry文の中へ入れて初期化された場合にのみ使われるようにします。

package sample23;
public class Sample01 {
    //----- ここから
    public static void main(String[] args) {
        java.io.BufferedReader in = new java.io.BufferedReader(
                                    new java.io.InputStreamReader(System.in));
        System.out.print("Input name:");
        String name;
        try {
            name = in.readLine();
            System.out.println("Hello, "+name);
        } catch (java.io.IOException e) {
            e.printStackTrace();
        } finally {
            try {
              in.close();
            } catch (java.io.IOException e) {
              e.printStackTrace();
            }
        }
    }
    //----- ここまで
}

 以上のようにEclipseを使っている場合は、まずは行いたい処理をとにかくコーディングしてしまいましょう。赤い下線が表示され、try文を使っていないことからエラーが発生していると分かった場合には、上記のような方法でtry文を自動生成すると、簡単に例外へ対応するコードを記述できます。

package sample23;
public class Sample01 {
    //----- ここから
    public static void main(String[] args) {
        java.io.BufferedReader in = new java.io.BufferedReader(
                                    new java.io.InputStreamReader(System.in));
        System.out.print("Input name:");
        String name;
        try {
            name = in.readLine();
            System.out.println("Hello, "+name);
        } catch (java.io.IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }
        }
    }
    //----- ここまで
}

注意! 実開発では詳細に記述する

本稿のサンプルでは例外をキャッチしたときに、エラー発生時にどのようなメソッドがどんな順番で呼ばれてエラーとなったのかを追跡できるように「e.printStackTrace();」というスタック情報を出力するコードを書いていますが、通常は適切なエラーメッセージを出力したり、ログ情報の出力をしたりするコードを記述します。

また、文法的には空行とすることもできますが、プログラム上の例外的な何かが発生しているわけですから、それに対して有効な何らかの対応をするコードを記述するようにしましょう。


例外処理の書き方

 次は、この例外へ対処するコードについて文法的に理解をしましょう。Sample01.javaでコーディングしたような「例外へ対処するコード」は「try文」といいます。try文は次のように書きます。

try {
    行いたい処理の文
} catch (例外型1 e1) {
    例外型1の例外が発生した場合に行いたい処理の文
} catch (例外型2 e2) {
    例外型2の例外が発生した場合に行いたい処理の文
} finally {
    例外が発生してもしなくても必ず行う処理の文
}

 tryの直後のブロックを「tryブロック」、finallyの直後のブロックを「finallyブロック」といいます。tryブロックの直後には「catch節」を含めることができます。

catch節「例外ハンドラ」

 catch節は「例外ハンドラ」と呼ばれることもあります。catch節では「(例外型1 e1)」のように例外パラメータを1つだけ指定する必要があります。このパラメータはcatch節の直後にあるブロック内で有効となります。例外パラメータの型はjava.lang.Throwableのサブクラスである必要があります。

注意! java.lang.Throwableクラスは指定してはいけない  

例外パラメータの型は、文法的にはjava.lang.Throwableクラスか、このクラスを継承したクラスであればいいのですが、実際にはjava.lang.Throwableクラスを指定することはありません。

このクラスを指定すると、後述するキャッチすべきではない「java.lang.Error」もキャッチすることになるからです。


 tryブロック、つまり「行いたい処理の文」の中で例外が発生するメソッドがある場合は、その例外に応じてcatch節を記述します。「行いたい処理の文」は複数の文を書けるので、キャッチする必要がある例外も複数あり得ます。このため、catch節は例外の型に応じて複数指定できるようになっています。

finallyブロック

 finallyブロックは「例外が発生してもしなくても必ず行う処理」を記述するために使います。

 例えば、入出力ストリームを使用するプログラムでは、ある入出力ストリームを使ったら、その入出力ストリームを閉じるcloseメソッドを必ず呼び出す必要があります。これは、入出力ストリームはOSによって管理される資源(リソース)ですから、OSへ必ず使い始めることと、使い終わったことを知らせる必要があるからです。

 このため、入出力ストリームを使用している最中に例外が発生した場合でも、必ず終了処理であるcloseメソッドを呼び出す必要があります。Sample01クラスにおいて、「in.close();」の処理は特別だと書きましたが、こういった特別な事情があったので、finallyブロックの中へ入れました。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。