Javaの例外、パート2:高度な機能とタイプ

JDK 1.0では、予期されるプログラムの動作とは異なる例外を処理するための言語機能とライブラリタイプのフレームワークが導入されました。このチュートリアルの前半では、Javaの基本的な例外処理機能について説明しました。この後半では、JDK 1.0とその後継であるJDK1.4、JDK 7、およびJDK 9によって提供されるより高度な機能を紹介します。スタックトレース、原因、例外チェーンなどの高度な機能を使用して、Javaプログラムの例外を予測および管理する方法を学びます。 -with-resources、multi-catch、final re-throw、およびstackwalking。

このチュートリアルのコード例はJDK12と互換性があることに注意してください。

ダウンロードコードを取得するこのチュートリアルのサンプルアプリケーションのソースコードをダウンロードします。JavaWorld用にJeffFriesenによって作成されました。

JDK 1.0および1.4での例外処理:スタックトレース

各JVMスレッド(実行パス)は、スレッドの作成時に作成されるスタックに関連付けられています。このデータ構造は、メソッド呼び出しに関連付けられたデータ構造であるフレームに分割されます。このため、各スレッドのスタックは、メソッド呼び出しスタックと呼ばれることがよくあります

メソッドが呼び出されるたびに、新しいフレームが作成されます。各フレームには、ローカル変数、パラメーター変数(メソッドに渡される引数を保持する)、呼び出し元のメソッドに戻るための情報、戻り値を格納するためのスペース、例外のディスパッチに役立つ情報などが格納されます。

スタックトレース(としても知られているスタックのバックトレースは)スレッドの実行中のある時点でアクティブなスタックフレームの報告です。JavaのThrowableクラス(java.langパッケージ内)は、スタックトレースを出力し、スタックトレースに入力し、スタックトレースの要素にアクセスするためのメソッドを提供します。

スタックトレースの印刷

ときthrow文がスロー可能オブジェクトをスローし、それが最初に適し探しcatch実行する方法でブロック。見つからない場合は、メソッド呼び出しスタックを巻き戻しcatch、例外を処理できる最も近いブロックを探します。見つからない場合、JVMは適切なメッセージで終了します。リスト1を検討してください。

リスト1. PrintStackTraceDemo.java(バージョン1)

import java.io.IOException; public class PrintStackTraceDemo { public static void main(String[] args) throws IOException { throw new IOException(); } }

リスト1の不自然な例では、java.io.IOExceptionオブジェクトを作成し、このオブジェクトをmain()メソッドからスローします。のでmain()、このスロー可能オブジェクトを処理していない、とするのでmain()、トップレベルの方法で、JVMは、適切なメッセージを表示して終了します。このアプリケーションの場合、次のメッセージが表示されます。

Exception in thread "main" java.io.IOException at PrintStackTraceDemo.main(PrintStackTraceDemo.java:7)

JVMを呼び出すことによって、このメッセージを出力するThrowablevoid printStackTrace()呼び出しのスタックトレース印刷法、Throwable標準エラーストリームにオブジェクトを。最初の行は、throwableのtoString()メソッドを呼び出した結果を示しています。次の行は、fillInStackTrace()(後で説明する)によって以前に記録されたデータを示しています。

追加の印刷スタックトレース方法

Throwableのオーバーロードさvoid printStackTrace(PrintStream ps)れたvoid printStackTrace(PrintWriter pw)メソッドは、スタックトレースを指定されたストリームまたはライターに出力します。

スタックトレースは、スローアブルが作成されたソースファイルと行番号を明らかにします。この場合、PrintStackTrace.javaソースファイルの7行目に作成されています。

printStackTrace()通常はcatchブロックから直接呼び出すことができます。たとえば、PrintStackTraceDemoアプリケーションの2番目のバージョンについて考えてみます。

リスト2. PrintStackTraceDemo.java(バージョン2)

import java.io.IOException; public class PrintStackTraceDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { ioe.printStackTrace(); } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

リスト2は、main()methodを呼び出すメソッドa()を示していますb()。このメソッドはmethodを呼び出します。メソッドb()IOExceptionオブジェクトをJVMにスローし、JVMは、例外を処理できるmain()catchブロックが見つかるまでメソッド呼び出しスタックを巻き戻します。例外はprintStackTrace()、throwableを呼び出すことによって処理されます。このメソッドは、次の出力を生成します。

java.io.IOException at PrintStackTraceDemo.b(PrintStackTraceDemo.java:24) at PrintStackTraceDemo.a(PrintStackTraceDemo.java:19) at PrintStackTraceDemo.main(PrintStackTraceDemo.java:9)

printStackTrace()スレッド名を出力しません。代わりtoString()に、throwableを呼び出して、throwableの完全修飾クラス名(java.io.IOException)を返します。これは、最初の行に出力されます。次に、メソッド呼び出し階層を出力します。最近呼び出されたメソッド(b())が一番上にありmain()、一番下にあります。

スタックトレースはどの行を識別しますか?

スタックトレースは、スローアブルが作成される行を識別します。throwスローアブルが作成されたのと同じ行にスローされない限り、スローアブルがスローされる行を(経由で)識別しません。

スタックトレースの入力

ThrowableThrowable fillInStackTrace()実行スタックトレースを埋めるメソッドを宣言します。呼び出し元Throwableオブジェクトには、現在のスレッドのスタックフレームの現在の状態に関する情報が記録されます。リスト3を検討してください。

リスト3. FillInStackTraceDemo.java(バージョン1)

import java.io.IOException; public class FillInStackTraceDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { ioe.printStackTrace(); System.out.println(); throw (IOException) ioe.fillInStackTrace(); } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

リスト3とリスト2の主な違いは、catchブロックのthrow (IOException) ioe.fillInStackTrace();ステートメントです。このステートメントはioe、のスタックトレースを置き換え、その後、スロー可能オブジェクトが再スローされます。次の出力を確認する必要があります。

java.io.IOException at FillInStackTraceDemo.b(FillInStackTraceDemo.java:26) at FillInStackTraceDemo.a(FillInStackTraceDemo.java:21) at FillInStackTraceDemo.main(FillInStackTraceDemo.java:9) Exception in thread "main" java.io.IOException at FillInStackTraceDemo.main(FillInStackTraceDemo.java:15)

IOExceptionオブジェクトが作成された場所を識別する最初のスタックトレースを繰り返す代わりに、2番目のスタックトレースはの場所を明らかにしますioe.fillInStackTrace()

スロー可能なコンストラクターと fillInStackTrace()

の各Throwableコンストラクターはを呼び出しますfillInStackTrace()。あなたが通過するときただし、(JDK 7で導入された)次のコンストラクタは、このメソッドを呼び出しませんfalsewritableStackTrace

Throwable(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace)

fillInStackTrace()現在のスレッドのメソッド呼び出しスタックをウォークダウンするネイティブメソッドを呼び出して、スタックトレースを構築します。このウォークは費用がかかり、頻繁に発生するとパフォーマンスに影響を与える可能性があります。

パフォーマンスが重要な状況(おそらく組み込みデバイスが関係する)に遭遇した場合は、をオーバーライドすることでスタックトレースが構築されないようにすることができますfillInStackTrace()。リスト4をチェックしてください。

リスト4. FillInStackTraceDemo.java(バージョン2)

{ public static void main(String[] args) throws NoStackTraceException { try { a(); } catch (NoStackTraceException nste) { nste.printStackTrace(); } } static void a() throws NoStackTraceException { b(); } static void b() throws NoStackTraceException { throw new NoStackTraceException(); } } class NoStackTraceException extends Exception { @Override public synchronized Throwable fillInStackTrace() { return this; } }

リスト4はを紹介しNoStackTraceExceptionます。このカスタムチェックされた例外クラスは、呼び出し元への参照をfillInStackTrace()返すようにオーバーライドします。このプログラムは、次の出力を生成します。thisThrowable

NoStackTraceException

オーバーライドするfillInStackTrace()メソッドをコメントアウトすると、次の出力が表示されます。

NoStackTraceException at FillInStackTraceDemo.b(FillInStackTraceDemo.java:22) at FillInStackTraceDemo.a(FillInStackTraceDemo.java:17) at FillInStackTraceDemo.main(FillInStackTraceDemo.java:7)

スタックトレースの要素へのアクセス

ロギングに必要な詳細を抽出したり、リソースリークの原因を特定したりするために、スタックトレースの要素にアクセスする必要がある場合があります。printStackTrace()およびfillInStackTrace()方法は、このタスクをサポートしていませんが、JDK 1.4を導入java.lang.StackTraceElementし、この目的のためにそのメソッド。

The java.lang.StackTraceElement class describes an element representing a stack frame in a stack trace. Its methods can be used to return the fully-qualified name of the class containing the execution point represented by this stack trace element along with other useful information. Here are the main methods:

  • String getClassName() returns the fully-qualified name of the class containing the execution point represented by this stack trace element.
  • String getFileName() returns the name of the source file containing the execution point represented by this stack trace element.
  • int getLineNumber() returns the line number of the source line containing the execution point represented by this stack trace element.
  • String getMethodName() returns the name of the method containing the execution point represented by this stack trace element.
  • boolean isNativeMethod() returns true when the method containing the execution point represented by this stack trace element is a native method.

JDK 1.4 also introduced the StackTraceElement[] getStackTrace() method to the java.lang.Thread and Throwable classes. This method respectively returns an array of stack trace elements representing the invoking thread's stack dump and provides programmatic access to the stack trace information printed by printStackTrace().

Listing 5 demonstrates StackTraceElement and getStackTrace().

Listing 5. StackTraceElementDemo.java (version 1)

import java.io.IOException; public class StackTraceElementDemo { public static void main(String[] args) throws IOException { try { a(); } catch (IOException ioe) { StackTraceElement[] stackTrace = ioe.getStackTrace(); for (int i = 0; i < stackTrace.length; i++) { System.err.println("Exception thrown from " + stackTrace[i].getMethodName() + " in class " + stackTrace[i].getClassName() + " on line " + stackTrace[i].getLineNumber() + " of file " + stackTrace[i].getFileName()); System.err.println(); } } } static void a() throws IOException { b(); } static void b() throws IOException { throw new IOException(); } }

When you run this application, you'll observe the following output:

Exception thrown from b in class StackTraceElementDemo on line 33 of file StackTraceElementDemo.java Exception thrown from a in class StackTraceElementDemo on line 28 of file StackTraceElementDemo.java Exception thrown from main in class StackTraceElementDemo on line 9 of file StackTraceElementDemo.java

最後に、JDK1.4はこのsetStackTrace()メソッドをに導入しましたThrowable。このメソッドは、リモートプロシージャコール(RPC)フレームワークやその他の高度なシステムで使用するように設計されており、クライアントfillInStackTrace()は、スローアブルが構築されたときに生成されるデフォルトのスタックトレースをオーバーライドできます。

以前fillInStackTrace()、スタックトレースが構築されないようにオーバーライドする方法を示しました。代わりに、あなたが使用して、新しいスタックトレースをインストールすることができますStackTraceElementし、setStackTrace()StackTraceElement次のコンストラクターを介して初期化されたオブジェクトの配列を作成し、この配列をsetStackTrace():に渡します。

StackTraceElement(String declaringClass, String methodName, String fileName, int lineNumber)

リスト6はとを示しStackTraceElementていsetStackTrace()ます。