例外

  1. 例外の目的
  2. 例外の発生方法
  3. Throwable クラスのメソッド
  4. どのような状況で例外をつかうか?

  1. 例外の目的
  2. 例外は、 エラーが発生したときに制御の流れを変更するための機構です。

    例外は、明示的に throw 文で起動することもできますし、 非明示的にアクションを実行することで起動することもできます。

    プログラム内にエラーから回復するルーチンがあれば、 制御をそこに移します。 プログラムの実行を開始した一番上にたどりついてもまだ、 その例外に対応するハンドラがなければ、 メッセージを出してプログラムの実行を停止します。

    java には、以下に示す3種類の例外があります。 一般に入力エラーやプログラムが処理を続行できない場合に、例外を発生させます。 プログラムの中で処理しなければならない例外は、クラス定義例外です。

    java では、スタックトレースの出力に続いて、 プログラムが終了することがクラッシュに相当します。

    例外の種類 属    性 継承クラス
    ランタイム実行中に発生するもの RuntimeExceptionNullPointerException
    エラー プログラムエラー ErrorThreadDeath
    クラス定義クラスで定義したもの ExceptionNoSuchMethodException

    java の例外はオブジェクトなので、メソッド、タイプ、データを持てます。 例外をオブジェクトとすることによって、 例外を報告し、復帰するのに必要なデータやメソッドを持てます。

    例外オブジェクトは Throwable クラスを拡張したクラスでなければなりません。 また例外オブジェクトは通常 Exception クラスを拡張したクラスで、 Exception クラスにはエラーを表現する文字列フィールドがあります。

    java Core API の java.lang.Throwable と java.lang.Exception を見てみましょう。

    例 NullPointerException

    // causes an exception by referencing beer[3]
    // before it has been assigned a value.
    
    class test5e {
      public static void main (String ar[]) {
          int a=0; int b, c, d =3, i;
          Integer beer[] = new Integer[10];
          System.out.println("a=" +a );
    
          a=beer[3].intValue();
      }
    }
    
    osaka-80] java test5e
    a=0
    java.lang.NullPointerException
            at test5e.main(Compiled Code)
    

    質問このプログラムは、どうして例外が発生したのでしょうか?

  3. 例外の発生方法
  4. 例外は、次の方法で発生させられます。

    例外の発生 throw 例外オブジェクト;

    例外オブジェクトは、 java.lang.Exception を拡張したクラスのオブジェクトです。

    例外の処理
    try {
         ブロック
    } catch (例外1) {
         例外ブロック1
    } catch (例外2) {
         例外ブロック2
    } .....
       :
    } finally {
         すべてに共通の処理
    }
    

    メソッドは、例外の処理として内部で生じる例外をキャッチ (catch)するか、 シグネチャの一部として宣言することで外部に例外を外に投げる(throws)か、 のどちらかをしなければなりません。

    シグネチャの一部として宣言するには、以下のようにします。 シグネチャの一部として宣言するとは、投げられた例外を処理しないで投げることを、 意味します。

      宣言   アクセス修飾子 戻り値の型 メソッド名(引数)throws e1,e2,e3 ... {}
    

    例1 ユーザ定義例外

    OutOfGas は、java.lang.Exception を拡張したユーザ定義例外クラスです。

    // some exception handling
    
    class test5j {
       public static void main(String[] a) throws OutOfGas {
          apple();
       }
    
       public static void apple() {
          int i=1, j=0, k;
    
          try {
             if (j<1) throw (new OutOfGas() );
             k= i/j;
          }
          catch ( OutOfGas o) {
              System.out.println("In catch clause"); int l=1;}
          finally { i=2;
              System.out.println("In finally clause"); int l=1;}
       }
    }
    
    class OutOfGas extends Exception {}
    
    osaka-84] java test5j
    In catch clause
    In finally clause
    

  5. Throwable クラスのメソッド
  6. すでにある例外のサブクラスを作って新しい例外を作成する時、 メッセージ文字列を例外に結びつけることができます。 そのためには Throwable(String) コンストラクタに文字列を渡す、 コンストラクタを書かなければなりません。 そうすることで、 getMessage メソッドを使って、 そのメッセージ文字列を取得できます。 通常メッセージ文字列は、問題を解決する助けになります。

    Throwable クラスから継承されるもう1つのメソッドに printStackTrace があります。 例外に対してこのメソッドを実行すると、 例外が投げられた所までの呼び出しチェーンを表示できます。

    例2 ユーザ定義例外

    OutOfGas は、java.lang.Throwable のサブクラス java.lang.Exception を拡張したユーザ定義例外クラスです。

    class test5m {
       public static void main(String[] a) throws OutOfGas {
          apple();
       }
    
       public static void apple() {
          int i=1, j=0, k;
    
          try {
            if (j<1) throw (new OutOfGas("try the reserve tank") );
            k= i/j;
          }
          catch ( OutOfGas o) {
             System.out.println("caught exception!");
             System.out.println( o.getMessage() );
             o.printStackTrace();
          }
          finally {System.out.println("in the finally clause.");}
       }
    }
    
    class OutOfGas extends Exception {
         OutOfGas(String s) {super(s);}
    
    osaka-29] java test5m
    caught exception!
    try the reserve tank
    OutOfGas: try the reserve tank
            at java.lang.Throwable.(Compiled Code)
            at java.lang.Exception.(Compiled Code)
            at OutOfGas.(Compiled Code)
            at test5m.apple(Compiled Code)
            at test5m.main(Compiled Code)
    in the finally clause.
    

    説明 上記プログラムについて簡単に説明します。

    printStackTrace メソッドで、例外が起きたときのスタックをトレースします。 メソッド名の隣が (Compiled Code) となっているのは、JIT で実行中のためです。 JIT でない場合は、該当するソースの行番号がでます。

    JIT(Just In Time) compile とは、 ある部分のコードをコンパイルしてバイトコードを生成しておき、 それを実行することです。 java の実行速度を上げるための便法です。

  7. どのような状況で例外をつかうか?
  8. 例外は予想がつく単純な状況で使われるものではありません。 例を考えてみましょう。

    例えば入力を考えてみましょう。 いつかは入力ストリームの最後にたどり着くことは解かっています。 そのためストリームの次の入力を返すメソッドにとって、 終わりに遭遇するのは当然です。 つまり、そのようなメソッドが戻り値として、入力の終わりを返すのは自然です。 また呼び出し側がその戻り値を調べるのも理解しやすいでしょう。

       while ((token = stream.next()) != Stream.END)
          process(token);
       stream.close();
    

    これと、入力の終わりを例外で知らせるものとを比較してみよう。

        try {
           for(;;)  {
              process(stream.next());
           }
        }  catch (StreamEndException e) {
           stream.close();
        }
    

    2番目のコードの不自然さがよく解かります。 最も自然な設計は、ストリームを読み出す前に常に EOF を調べるメソッドを呼び出すことです。

       while (! stream.eof())
          process(stream.next());
       stream.close();
    





まとめ

例外の目的

例外の処理

例外未処理宣言 アクセス修飾子 戻り値の型 メソッド名(引数)throws e1,e2,e3 ... {}
ただし e1,e2,e3 ... は、例外クラス名
例外の発生 throw 例外オブジェクト;

例外オブジェクトは、 java.lang.Exception を拡張したクラスのオブジェクト

例外の処理
try {
     ブロック
} catch (例外1) {
     例外ブロック1
} catch (例外2) {
     例外ブロック2
} .....
   :
} finally {
     すべてに共通の処理
}