目次へ戻る 下へ↓
6、Javaの例外処理機構
作成者:Fumitaka Makino 更新日:2003-05-22 13:50

・Exceptionとは?

最近のJavaを含むモダンなプログラミング言語には必ず例外処理機構が存在します。ほとんどのプログラムはある特定の状態であることが求められます。例えるなら、高校生になるためには中学校を卒業している必要があり、中学生になるためには小学校を卒業している必要があるということです。ところがその特定の状態、つまり条件を満たしていなかったり、望ましくない状態(例外状態)であった時に、例外処理機構をもたない言語は処理を中断してしまいます。処理が突然中断してしまう(バグと呼ばれる場合もある)のでどこで中断したのかも、何で中断したのかもわかりません。そのためその部分を突き止めるために多くの時間を費やさなければならないのです。そのような状態を未然に防ぐため例外に対する対策をあらかじめ講じておき、何か起きたときに例外用の処理を行ったり、どこで例外が発生したかを容易に判明するようにするための機構を例外処理機構と呼びます。

Javaではこの例外処理機構をException(イクセプション)という形で実装しています。Exceptionとはクラスで例外を表すクラスで、全ての種類のExceptionは必ずjava.lang.Exceptionクラスを継承(第7回を参照)しています。下図のようにExceptionはJavaのプログラムの実行中に何らかの問題が発生すると生成されます。生成されたExceptionは全ての処理に優先して通知(throwされると言う)が行われます。

またこのExceptionとはクラスなので、下記のようにプログラマーが自分でExceptionのインスタンスを生成して問題が発生したことを通知する(throwする)ことができます。

throw new IOException("問題発生");

このようにあるメソッド内でthrowしたException、もしくは利用しているクラスのメソッドからthrowされる可能性のあるExceptionに対しては下記のいずれかの方法、もしくは双方で対処しなければいけません。

  • try/catch文により例外に対する処理を記述する。
  • 発生した例外に対する処理を自分(自メソッド)の呼び元、つまり上位に依頼する。

上記のいずれかの対処をしない限り、そのソースコードは下記のようなエラーメッセージが出てコンパイルすることができません。


 jp\co\wownet\sample\Controller.java:54: 例外 java.lang.ClassNotFoundException は
 報告されません。スローするにはキャッチまたは、スロー宣言をしなければなりません。

             Class.forName(driver);
                ^

つまりExceptionとは、あるメソッドが何らかの例外を発生する可能性がある時は、どんな形であれ必ず呼び出し元のクラスにその例外に対する対応を強制するのです。このため、Javaにおいてコンパイルされたクラスは、プログラマーが故意に例外の可能性を無視しない限りは必ず例外に対する対処が行われているのです。


・try/catch文

では、実際の具体的な処理として、throwされるExceptionに対処するためのtry/catch節を説明します。下記のように

    try {
      ・・・・・ 例外が発生する可能性のあるコード
    } catch( Exception e ) {//対処する例外の種類: e はcatch節内で補足したExceptionを扱う際の変数名
      ・・・・・ 例外に対する対処コード
    }

を記述します。このとき例外が発生する可能性のあるコードはtryとcatchの間に記述し、対処する(捕らえる)例外の種類をcatch節で宣言します。そしてcatch節の内部で捕らえた例外に対する対処コードを記述します。実際には以下のように記述します。下記のコードはインスタンスが存在しないクラス型の変数に対してメソッドを実行しているので、substringメソッドを実行した時にNullPointerExceptionという例外がthrowされてきます。

例:例外発生時の対処

     
 

try{
  String str = null;
  str = str.substring(1);//この部分でNullPointerExceptionが発生
  System.out.println( str );
} catch( NullPointerException e ){//補足対象のExceptionを宣言
  // 対象となる例外が発生しなければ実行されない

  e.printStackTrace();//Exceptionクラスのメソッドで例外の詳細を出力するメソッドを実行
  System.out.println( "ヌルポンが発生したようです。" );
}

 
     

この時、重要なのが飽くまでも補足対象の例外はNullPointerExceptionのみだと言うことです。仮にtry節の内部でIOExceptionがthrowされたとしても上記のcatch節では補足されません。しかし、もしcatch節を以下のように宣言したとしたら全ての例外を補足することができます。

} catch( Exception e ) {

これは、なぜでしょうか?実は全ての種類のExceptionはjava.lang.Exceptionクラスを継承しているのです。継承については今後詳しく説明しますが、IOExceptionもNullPointerExceptionもExceptionを継承しているということです。つまりExceptionクラスの機能を拡張してNullPointerExceptionクラスは作られているためExceptionクラスの機能は全て持っているのです。もちろんIOExceptionに関しても同様です。ところが下図を見て下さい。NullPointerExceptionとIOExceptionは互いに継承関係にはないためNullPointerExceptionをIOExceptionとして扱うことや、その逆はできません。

図:Exceptionの継承図



IOException,RuntimeException,NullPointerExceptionはExceptionとして扱うことができます。またNullPointerExceptionはRuntimeExceptionとして扱うことができます。

またcatch節は一つだけでなく下記のように複数記述することもできます。この時注意しなければならないのが、上のcatch節から実行されていくと言うことです。つまり判定の順番がIOException→NullPointerException→Exceptionと言うことです。

例:例外発生時の対処2

     
 

try{
  ・・・・・
} catch( IOException e ){//最初に補足
  e.printStackTrace();
  System.out.println( "IOで問題が発生したようです。" );
} catch( NullPointerException e ){//2番目に補足
  e.printStackTrace();
  System.out.println( "ヌルポンが発生したようです。" );
} catch( Exception e ){//3番目に補足
  e.printStackTrace();
  System.out.println( "何らかの問題が発生したようです。" );
}

 
     

これらのtry/catch節により予想される例外に対して対処ができるだけでなく、予想外の例外が発生した時にも迅速に特定することができます。また、try文にはcatchだけでなくfinally節というものもあります。finally節とは、例外が発生しようがしまいがtry文を抜ける時には必ず実行されます。後に学ぶデータベースとのコネクションなどは何が起きようと必ずクローズ処理をしなければならないのでfinally節でクローズ処理を記述します。

例:例外発生時の対処3 finally節

     
 

try{
  ・・・・・
} catch( Exception e ){//3番目に補足
  e.printStackTrace();
  System.out.println( "何らかの問題が発生したようです。" );
} finally {//例外が発生してもしなくても必ず実行される
  //必ず行わなければならない処理を記述
  con.close(); //例えばコネクションのクローズとか
}

 
     

 

 

・throwとthrowsによる上位クラスへの処理の委譲

自分で作ったクラスやメソッドで任意に例外を発生させる。もしくは発生した例外を呼び元に対して通知する方法について学びましょう。実際には前項のtry/catch文とあわせて使うのが普通です。前項で述べたようにExceptionは特殊な役割を持ってはいますが、コード的にはただのクラスです。そのため普通のクラスのようにインスタンスを生成することができます。そして例外が発生したことを通知するためにはJavaでは数少ない予約語である命令のthrow句を利用します。もし新たに任意のIOException を発生されるとしたら下記のように記述します。

throw new IOException( "例外発生" );

また、メソッド内部で発生した例外を呼び出し元に対して通知する(つまりExceptionを発生したメソッドの外にthrowする)ためには、throws句を利用してメソッドの宣言文の後に発生する可能性のある例外を記述します。

public void test() throws IOException{

上記のような宣言により例外が発生する可能性があるメソッドは呼び出し元に対して、その例外に対する対応を強制することが可能となります。つまり上記の場合はtestメソッドを利用するコードはIOExceptionに関してのtry/catch文を用意するか、呼び出しもとのメソッドで更にthrowsして処理を委譲しなければなりません。一般的にはtry/catch文とthrows句を利用して以下のようなコードを記述することが多いです。

例:例外発生時の対処4

     
 

public void test() throws IOException{//このメソッドはIOExceptonがthrowされる可能性があると宣言
  try{

    ・・・・・
  } catch( IOException e ){//最初に補足
    e.printStackTrace();
    System.out.println( "IOで問題が発生したようです。" );
    throw e; //IOExceptionなのでそのままスロー
  } catch( NullPointerException e ){//2番目に補足
    e.printStackTrace();
    System.out.println( "ヌルポンが発生したようです。" );
    throw new IOException( e.getMessage() );//新たにIOExceptionとしてスロー
  } catch( Exception e ){//3番目に補足
    e.printStackTrace();
    System.out.println( "何らかの問題が発生したようです。" );
    throw new IOException( e.getMessage() );
  }
}

 
     

 

 
 
 
↑上へ 目次へ戻る