Since: May./04th/2005
例外クラスのインスタンス化で挙げたとおり、例外を新規生成すると、スタックトレースは生成された場所でのものになります。詳細メッセージも、明示的に指定しない限り含まれません。
これではデバッグが難しいので、Java2 1.4以上では、捕捉した情報を、新たにnewするオブジェクトにセットできるようになっています。この機構を、例外チェーンと呼びます。例外チェインの実装には、全ての例外オブジェクトのスーパークラスである java.lang.Throwable
に追加されたメソッド initCause()
か、コンストラクタ Throwable(Throwable cause)
/Throwable(String message, Throwable cause)
を使います。
次のリストは実行時引数 args[0]
をプリントするだけのものです。実行時引数が与えられないと、非チェック例外 java.lang.ArrayIndexOutOfBoundsException
が発生します。発生した例外を try-catch
構文で捕捉していますが、新規に例外オブジェクト newEx
を生成しています。新規に生成されたばかりのオブジェクト newEx
には、実際に発生した例外情報は含まれていません。ここでは、キャッチした元の例外を、メソッド Throwable#initCause()
でセットしています。
出力結果を見ると、一度目の printStackTrace()
では、当該例外オブジェクトが生成された場所の情報しか含まれていないのに対して、initCause()
後の printStackTrace()
では、キャッチされた例外情報が "Caused by:
" 以下に追加されていることが分かります。
ここでは Throwable
型オブジェクトを、initCause()
でセットしましたが、例外オブジェクトのコンストラクタ引数に指定しても同様です。こうしてセットされた原因例外オブジェクトは、メソッド getCause()
でも取得可能です。
class ExceptionChainDemo { public static void main(String[] args) { try { System.out.println(args[0]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("例外の新規作成"); Exception newEx = new Exception(); newEx.printStackTrace(); System.out.println("例外に原因追加"); newEx.initCause(e); newEx.printStackTrace(); System.out.println("原因の抽出"); (newEx.getCause()).printStackTrace(); } } }
>javac ExceptionChainDemo.java >java ExceptionChainDemo 例外の新規作成 java.lang.Exception at ExceptionChainDemo.main(ExceptionChainDemo.java:7) 例外に原因追加 java.lang.Exception at ExceptionChainDemo.main(ExceptionChainDemo.java:7) Caused by: java.lang.ArrayIndexOutOfBoundsException: 0 at ExceptionChainDemo.main(ExceptionChainDemo.java:4) 原因の抽出 java.lang.ArrayIndexOutOfBoundsException: 0 at ExceptionChainDemo.main(ExceptionChainDemo.java:4)
initCause()
を使わずに、コンストラクタ引数で原因例外を与えることもできます。
SDK 1.3 以下の場合は、例外クラスのコンストラクタは、文字列しか引数に取れないので、次のように実装していました。
try {
// チェック例外IOExceptionが発生するかもしれない処理
FileReader obj = new FileReader("filename.txt");}
} catch (IOException e) {
// IOExceptionを捕捉して非チェック例外RuntimeExceptionでラップしてスロー
throw new RuntimeException(e.toString());
}
SDK 1.4 以上では、コンストラクタで原因例外を指定するには、Throwable(Throwable cause)
型のコンストラクタを使うことができます。
try {
// チェック例外IOExceptionが発生するかもしれない処理
FileReader obj = new FileReader("filename.txt");}
} catch (IOException e) {
// IOExceptionを捕捉して非チェック例外RuntimeExceptionでラップしてスロー
throw new RuntimeException(e);
}
自分で例外クラスを作成した場合は、super(Throwable cause)
などを指定する必要があります。次の場合は、RuntimeException
を継承して MyException
を作っています。コンストラクタ MyException(Throwable cause)
の中では、super(cause)
によって、RuntimeException
に実装されたコンストラクタ RuntimeException(Throwable cause)
を明示的に呼び出しています。
class MyException extends RuntimeException {
MyException(Throwable cause) {
super(cause);
}
}
super(cause)
は、 this.initCause(cause)
と書き換えても同様です。
次の例は、コンストラクタでラッピングを実現したものです。getResource()
がスローする FileNotFoundException
をキャッチして、ResourceNotFoundException
でラッピングしてスローしなおしています。
この例で定義しているのは、次の三つのクラスです。
ResourcesManager
getResource()
は、getFileResource()
を呼び出します。getFileResource()
では、チェック例外 java.io.FileNotFoundException
をスローしています。キャッチされた例外を、 ResourceNotFoundException
型でラップしています。ResourceNotFoundException
RuntimeExcepotion
を継承し、コンストラクタとして、引数に Throwable
型を受け取るものを定義しています。
ExceptionWrappingDemo
ResourceManager
をインスタンス化して、メソッド getResource()
を呼び出しています。
getResource()
実行時に FileNotFoundException
が発生するので、そのままであれば、getResource()
のメソッド宣言に throws
リストを定義して、呼び出し元で try-catch
する必要があります。しかし、ここでは、当該メソッド内の catch
節内において、非チェック例外でラッピングしているため、その必要はありません。
また、getResource()
メソッドの実装を変更して、データベース使用時の例外である SQLException
や、ネットワーク使用時の例外である SocketException
などがスローされるようになっても、呼び出し元が受け取るのは ResourceNotFoundException
型オブジェクトのままでよく、元々の例外情報はメソッド getCause()
で取得することができます。
import java.io.FileNotFoundException; class ResourcesManager { void getResource() { try { getFileResource(); } catch (FileNotFoundException e) { // 例外のラッピング ResourceNotFoundException ex = new ResourceNotFoundException(e); throw ex; } } void getFileResource() throws FileNotFoundException { throw new FileNotFoundException(); } } class ResourceNotFoundException extends RuntimeException { ResourceNotFoundException(Throwable cause) { super(cause); // 又は、this.initCause(cause); } } class ExceptionWrappingDemo { public static void main(String[ ] args) { ResourcesManager obj = new ResourcesManager(); obj.getResource(); } }
C:\java>javac ExceptionWrappingDemo.java C:\java>java ExceptionWrappingDemo Exception in thread "main" ResourceNotFoundException at ResourcesManager.getResource(ExceptionWrappingDemo.java:9) at ExceptionWrappingDemo.main(ExceptionWrappingDemo.java:29) Caused by: java.io.FileNotFoundException at ResourcesManager.getFileResource(ExceptionWrappingDemo.java:15) at ResourcesManager.getResource(ExceptionWrappingDemo.java:6) ... 1 more
"Caused by:
" の最終行の "... 1 more
" は、直前のスタックトレースの最終行を表します。よって、展開すると次のようになります。
Exception in thread "main" ResourceNotFoundException at ResourcesManager.getResource(ExceptionWrappingDemo.java:9) at ExceptionWrappingDemo.main(ExceptionWrappingDemo.java:29) Caused by: java.io.FileNotFoundException at ResourcesManager.getFileResource(ExceptionWrappingDemo.java:15) at ResourcesManager.getResource(ExceptionWrappingDemo.java:6) at ExceptionWrappingDemo.main(ExceptionWrappingDemo.java:29)
"Caused by:
" のスタックトレースを下から順番に読むと、 29 行目の "obj.getResource();
" から、6 行目の "getFileResource();
" に至り、更に 15 行目の "throw new FileNotFoundException();
" で例外 java.io.FileNotFoundException
が発生したことが分かります。
SEO | [PR] !uO z[y[WJ Cu | ||