異常繁殖
メソッドが例外をスローするときに、現在のメソッドが例外をキャッチしない場合、例外は、キャッチされたメソッドに遭遇するまで、上位の呼び出しメソッドにスローされますtry ... catch
。
// exception
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}
static void process1() {
process2();
}
static void process2() {
Integer.parseInt(null); // 会抛出NumberFormatException
}
}
printStackTrace()
メソッドの呼び出しスタックを出力すると、次のようになります。
java.lang.NumberFormatException: null
at java.base/java.lang.Integer.parseInt(Integer.java:614)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at Main.process2(Main.java:16)
at Main.process1(Main.java:12)
at Main.main(Main.java:5)
printStackTrace()
これはエラーのデバッグに非常に役立ちます。上記の情報は、メソッド内でスローされたことを示しています。下から上に、呼び出しレベルは次のとおりですNumberFormatException
。java.lang.Integer.parseInt
main()调用process1();
process1()调用process2();
process2()调用Integer.parseInt(String);
Integer.parseInt(String)调用Integer.parseInt(String, int)。
Integer.java のソース コードを見ると、例外をスローするメソッドのコードは次のとおりであることがわかります。
public static int parseInt(String s, int radix) throws NumberFormatException {
if (s == null) {
throw new NumberFormatException("null");
}
...
}
さらに、各レイヤーの呼び出しによりソース コードの行番号が与えられ、これを直接見つけることができます。
例外のスロー
ユーザーが不正な文字を入力した場合など、エラーが発生した場合、例外をスローできます。
例外をスローするにはどうすればよいですか? メソッドを参照してInteger.parseInt()
、次の 2 つの手順で例外をスローします。
a; throws withステートメントException
のインスタンスを作成します。以下に例を示します。throw
void process2(String s) {
if (s==null) {
NullPointerException e = new NullPointerException();
throw e;
}
}
实际上,绝大部分抛出异常的代码都会合并写成一行:
void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
メソッドが例外をキャッチし、catch 句で新しい例外をスローする場合、それはスローされた例外の型を「変換」することと同じです。
void process1(String s) {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException();
}
}
void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
process2()
投げて、NullPointerException
捕まえprocess1()
て、そして投げるIllegalArgumentException()
。
main()
に引っかかった場合はIllegalArgumentException
、出力された例外スタックを見てみましょう。
// exception
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}
static void process1() {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException();
}
}
static void process2() {
throw new NullPointerException();
}
}
出力される例外スタックは次のようになります。
java.lang.IllegalArgumentException
at Main.process1(Main.java:15)
at Main.main(Main.java:5)
NullPointerException
これは、新しい例外によって元の例外情報が失われ、元の例外の情報が表示されなくなったことを示しています。
完全な例外スタックを追跡できるようにするために、例外を構築するときに元の例外インスタンスが渡され、新しい例外は元の情報を保持できますException
。上記のコードの改良点は次のとおりです。
// exception
public class Main {
public static void main(String[] args) {
try {
process1();
} catch (Exception e) {
e.printStackTrace();
}
}
static void process1() {
try {
process2();
} catch (NullPointerException e) {
throw new IllegalArgumentException(e);
}
}
static void process2() {
throw new NullPointerException();
}
}
上記のコードを実行すると、出力される例外スタックは次のようになります。
java.lang.IllegalArgumentException: java.lang.NullPointerException
at Main.process1(Main.java:15)
at Main.main(Main.java:5)
Caused by: java.lang.NullPointerException
at Main.process2(Main.java:20)
at Main.process1(Main.java:13)
Caused by: Xxx
キャプチャされた内容はIllegalArgumentException
問題の根本原因ではなく、根本原因はそれがメソッドNullPointerException
にMain.process2()
スローされたことであることに注意してください。
コードで元の例外を取得するには、メソッドを使用できますThrowable.getCause()
。null を返した場合は、すでに「ルート例外」であることを意味します。
完全な例外スタック情報があれば、コードの問題を迅速に特定して修正できます。
例外がキャッチされ、再度スローされた場合は、元の例外を保持する必要があります。そうしないと、最初の犯罪現場を特定することが困難になります。
try または catch ブロックで例外をスローした場合、finally ステートメントは実行されますか? 例えば:
// exception
public class Main {
public static void main(String[] args) {
try {
Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("catched");
throw new RuntimeException(e);
} finally {
System.out.println("finally");
}
}
}
上記のコードの実行結果は次のようになります。
catched
finally
Exception in thread "main" java.lang.RuntimeException: java.lang.NumberFormatException: For input string: "abc"
at Main.main(Main.java:8)
Caused by: java.lang.NumberFormatException: For input string: "abc"
at ...
最初の行が出力されcatched
、ステートメント ブロックが入力されたことが示されますcatch
。2 行目が出力されfinally
、文ブロックが実行されたことが示されますfinally语
。
したがって、catch
例外をスローしてもfinall
y の実行には影響しません。JVM
が最初に実行されfinally
、その後例外がスローされます。
例外シールド
finally文の実行時に例外がスローされた場合、catch文の例外をスローし続けることができますか? 例えば:
// exception
public class Main {
public static void main(String[] args) {
try {
Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("catched");
throw new RuntimeException(e);
} finally {
System.out.println("finally");
throw new IllegalArgumentException();
}
}
}
上記のコードを実行すると、例外情報が次のとおりであることがわかります。
catched
finally
Exception in thread "main" java.lang.IllegalArgumentException
at Main.main(Main.java:11)
これは、最終的に例外をスローした後、スローできる例外は 1 つだけであるため、もともと catch でスローされるように準備されていた例外が「消える」ことを示しています。スローされない例外は、「マスクされた」例外と呼ばれます(Suppressed Exception)
。
まれに、すべての例外について知っておく必要があります。すべての例外情報を保存するにはどうすればよいですか? この方法では、最初に元の例外をorigin変数で保存し、次に を呼び出してThrowable.addSuppressed()
元の例外を追加し、最後にそれを最後にスローします。
// exception
public class Main {
public static void main(String[] args) throws Exception {
Exception origin = null;
try {
System.out.println(Integer.parseInt("abc"));
} catch (Exception e) {
origin = e;
throw e;
} finally {
Exception e = new IllegalArgumentException();
if (origin != null) {
e.addSuppressed(origin);
}
throw e;
}
}
}
catch とfinallyの両方が例外をスローする場合、catch例外はシールドされていますが、finallyによってスローされる例外には依然として例外が含まれています。
Exception in thread "main" java.lang.IllegalArgumentException
at Main.main(Main.java:11)
Suppressed: java.lang.NumberFormatException: For input string: "abc"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Integer.parseInt(Integer.java:652)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at Main.main(Main.java:6)
すべての n はによってThrowable.getSuppressed()
取得できますSuppressed Exceptio
。
ほとんどの場合、finally に例外をスローしないでください。したがって、通常は気にする必要はありませんSuppressed Exception
。
質問するときは、例外を投稿してください。
例外によって出力される詳細なスタック情報が、問題を見つける鍵となります。多くの初心者は、質問するときにコードのみを投稿し、例外は投稿しません。
質問するときに例外スタックを投稿しないようにと誰が教えましたか
一部の子供用靴には異常な情報のみが掲載され、最も重要な情報がCaused by: xxx
省略されている場合がありますが、これは間違った質問方法であり、変更する必要があります。
まとめ
この呼び出しではprintStackTrace()
例外伝播スタックを出力できます。これはデバッグに非常に役立ちます。
例外をキャッチし、再度新しい例外をスローする場合は、元の例外情報を保持する必要があります。
通常、finally
で例外をスローしません。最終的に例外がスローされた場合は、元の例外を元の例外に追加する必要があります。呼び出し元は、によってThrowable.getSuppressed()
追加されたすべてを取得できますSuppressed Exception
。