Try-catch構造には小さな理論的問題があります。
昨日、Javaについての実技試験を受けましたが、次の例は理解できません。
try {
try {
System.out.print("A");
throw new Exception("1");
} catch (Exception e) {
System.out.print("B");
throw new Exception("2");
} finally {
System.out.print("C");
throw new Exception("3");
}
} catch (Exception e) {
System.out.print(e.getMessage());
}
質問は「出力がどのように見えるか?」でした。
私はそれがAB2C3であると確信していましたが、驚きです、それは真実ではありません。
正解はABC3です(テスト済みで、実際はそのようです)。
私の質問は、Exception( "2")はどこに行きましたか?
Java Language Specification 14.20.2。 から:
Catchブロックが理由Rで突然完了した場合、finallyブロックが実行されます。次に、選択肢があります。
Finallyブロックが正常に完了すると、理由Rによりtryステートメントが突然完了します。
finallyブロックが理由Sで突然完了した場合、tryステートメントは理由Sで突然完了します(そして理由Rは破棄されます)。
そのため、例外をスローするcatchブロックがある場合:
_try {
// ...
} catch (Exception e) {
throw new Exception("2");
}
_
ただし、例外をスローするfinallyブロックもあります。
_} finally {
throw new Exception("3");
}
_
Exception("2")
は破棄され、Exception("3")
のみが伝播されます。
Finallyブロックでスローされた例外は、tryブロックまたはcatchブロックで以前にスローされた例外を抑制します。
Java 7の例: http://ideone.com/0YdeZo
Javadoc's の例から:
static String readFirstLineFromFileWithFinallyBlock(String path)
throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
ただし、この例では、メソッドreadLineおよびcloseが両方の例外をスローする場合、メソッドreadFirstLineFromFileWithFinallyBlockはfinallyブロックからスローされた例外をスローします。 tryブロックからスローされた例外は抑制されます。
新しい try-with
Java 7の構文は、例外抑制の別のステップを追加します。tryブロックでスローされた例外は、try-withパートで以前にスローされた例外を抑制します。
同じ例から:
try (
Java.util.Zip.ZipFile zf = new Java.util.Zip.ZipFile(zipFileName);
Java.io.BufferedWriter writer = Java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
) {
for (Java.util.Enumeration entries = zf.entries(); entries.hasMoreElements();) {
String newLine = System.getProperty("line.separator");
String zipEntryName = ((Java.util.Zip.ZipEntry)entries.nextElement()).getName() + newLine;
writer.write(zipEntryName, 0, zipEntryName.length());
}
}
Try-with-resourcesステートメントに関連付けられたコードブロックから例外をスローできます。上記の例では、tryブロックから例外をスローできます。また、ZipFileオブジェクトとBufferedWriterオブジェクトを閉じようとしたときに、try-with-resourcesステートメントから最大2つの例外をスローできます。 tryブロックから例外がスローされ、try-with-resourcesステートメントから1つ以上の例外がスローされる場合、try-with-resourcesステートメントからスローされる例外は抑制され、ブロックによってスローされる例外が1つになります。 writeToFileZipFileContentsメソッドによってスローされます。 tryブロックによってスローされた例外からThrowable.getSuppressedメソッドを呼び出すことで、これらの抑制された例外を取得できます。
質問からのコードでは、各ブロックは古い例外を明確に破棄しており、ログも記録していません。いくつかのバグを解決しようとしているときは良くありません:
throw new Exception("2");
はcatch
ブロックではなくtry
ブロックからスローされるため、再びキャッチされることはありません。
14.20.2。try-finallyおよびtry-catch-finallyの実行を参照してください。
これは何が起こっているかです:
try {
try {
System.out.print("A"); //Prints A
throw new Exception("1");
} catch (Exception e) {
System.out.print("B"); //Caught from inner try, prints B
throw new Exception("2");
} finally {
System.out.print("C"); //Prints C (finally is always executed)
throw new Exception("3");
}
} catch (Exception e) {
System.out.print(e.getMessage()); //Prints 3 since see (very detailed) link
}
あなたの質問は非常に明白で、答えは同じくらい簡単です。「2」のメッセージを持つ例外オブジェクトは、「3」のメッセージを持つ例外オブジェクトによって上書きされます。
説明:例外が発生すると、処理するブロックをキャッチするためにスローされたオブジェクト。ただし、catchブロック自体で例外が発生すると、そのオブジェクトは、例外処理のためにOUTER CATCHブロック(存在する場合)に転送されます。ここでも同じことが起こりました。メッセージ「2」の例外オブジェクトは、外部キャッチブロックに転送されます。 ただし待機 ..内部try-catchブロックを離れる前に、最後に実行する必要があります。ここで私たちが心配している変化が起こりました。新しいEXCEPTIONオブジェクト(メッセージ "3")がスローされるか、すでにスローされたExceptionオブジェクト(メッセージ "2")を置き換えるこの最終ブロック。その結果、Exceptionオブジェクトのメッセージが出力されると、オーバーライドされた値、つまり「2」ではなく「3」。
Keep Remember:CATCHブロックで処理できる例外オブジェクトは1つだけです。
finally
ブロックは常に実行されます。 tryブロック内からreturn
を実行するか、例外がスローされます。 finally
ブロックでスローされた例外は、catchブランチでスローされた例外をオーバーライドします。
また、例外をスローしても、それ自体では出力が発生しません。行throw new Exception("2");
は何も書き出しません。
あなたのコードによると:
try {
try {
System.out.print("A");
throw new Exception("1"); // 1
} catch (Exception e) {
System.out.print("B"); // 2
throw new Exception("2");
} finally { // 3
System.out.print("C"); // 4
throw new Exception("3");
}
} catch (Exception e) { // 5
System.out.print(e.getMessage());
}
あなたがここで見ることができるように:
# 1
をスローします;B - # 2
;によってキャッチされました。# 3
は、try-catch(または、例外が発生していない場合はtryのみ)ステートメントの後に実行され、C - # 4
を出力して新しい例外をスローします。# 5
;によってキャッチされました。結果はABC3
です。 2
は、1
と同じ方法で省略されます