キャッチされないJava例外?


170

私は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")はどこに行ったのですか?


8
+1ああ男、私はこの答えを知っていました。これはインタビューで尋ねられました。スタックでtry / catch / finallyがどのように機能するかを理解するには、非常に良い質問です。
しかし、私はラッパークラスではありません。2013年

10
数字を出力できるprintステートメントは1つだけです(最後の:)print(e.getMessage())。出力は次のようになるAB2C3と考えました。最も外側のcatchブロックが2回実行されると思いましたか?
Adrian Pronk 2013

Javaでは、catchブロックの外に制御を転送する命令が実行される前に、finallyブロックが存在する場合は、それが実行されます。finallyブロックのコードのみが制御を外部に転送しない場合、catchブロックからの遅延された命令が実行されます。
トーマス

回答:


198

Java言語仕様14.20.2。

理由Rが原因でcatchブロックが突然完了すると、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")伝播のみされます。


72
これはreturnステートメントにも当てはまります。最終ブロックに戻りがある場合、それはtryまたはcatchブロックの戻りをオーバーライドします。そのため、これらの「機能」の、良い習慣は、finallyブロックがなければならないということです決して例外をスローしないか、return文を持っています。
アウグスト

これは、Java 7でのtry-with-resourcesの継承の利点でもあります。リソースを閉じるときに2番目の例外が生成された場合、最初の例外が保持されるため、通常はデバッグが容易になります。
w25r 2013

19

finallyブロックでスローされた例外は、tryまたはcatchブロックで以前にスローされた例外を抑制します。

Java 7の例:http : //ideone.com/0YdeZo

Javadocの例から:


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-withJava 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ステートメントからスローされた例外は抑制され、ブロックによってスローされた例外はこれは、writeToFileZipFileContentsメソッドによってスローされます。これらの抑制された例外を取得するには、tryブロックによってスローされた例外からThrowable.getSuppressedメソッドを呼び出します。


問題のコードでは、各ブロックは古い例外を明らかに破棄しており、ログに記録することすらしておらず、いくつかのバグを解決しようとしているときには適切ではありません。

http://en.wikipedia.org/wiki/Error_hiding


9

からで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
}

はい、そうです、私はこれが起こっているのを見ます、しかし私は説明を探していました-なぜそれがこのように振る舞っているのですか
Kousalik

5

あなたの質問は非常に明白で、答えも同じくらい簡単です。 「2」というメッセージを持つ例外オブジェクトは、「3」というメッセージを持つ例外オブジェクトによって上書きされます。

説明: 例外が発生すると、そのオブジェクトがスローして、処理するブロックをキャッチします。ただし、catchブロック自体で例外が発生すると、そのオブジェクトは例外処理のためにOUTER CATCHブロック(存在する場合)に転送されます。そして、同じことがここで起こりました。メッセージ「2」の例外オブジェクトがOUTER catch Blockに転送されます。しかし、待ってください。内側のtry-catchブロックを終了する前に、最後に実行する必要があります。ここで私たちが懸念している変化が起こりました。新しいEXCEPTIONオブジェクト(メッセージ "3"を含む)がスローされるか、すでにスローされているExceptionオブジェクト(メッセージ "2"を含む)を置き換えた最終的にこのブロック。その結果、Exceptionオブジェクトのメッセージが出力されると、オーバーライドされた値、つまり「2」ではなく「3」。

覚えておいてください:CATCHブロックで処理できる例外オブジェクトは1つだけです。


2

finallyブロックは常に実行されます。どちらかあなたのreturntryブロックまたは例外の内側からがスローされます。finallyブロックでスローされた例外は、catchブランチでスローされた例外をオーバーライドします。

さらに、例外をスローしても、それ自体では何も出力されません。この行でthrow new Exception("2");は何も書き出されません。


1
はい、私は例外をスローすること自体は何も出力しないことを知っていますが、理由2をドロップする必要がある理由がわかりませんでした。私はもう少しスマートになりました:-)
Kousalik 2013

常に非常に長い時間であり、非常に長い時間で何かが発生する可能性があります(パズルwouter.coekaerts.be/2012/puzzle-dreamsをチェックしてください)
Dainius

0

あなたのコードによると:

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. Aを出力し、例外をスローします# 1
  2. この例外はcatchステートメントとprintによってキャッチされましたB - # 2
  3. ブロック# 3は、try-catch(または、例外が発生しなかった場合はtryのみ)ステートメントの後に最終的に実行され、C - # 4新しい例外を出力してスローします。
  4. これは外部のcatchステートメントによってキャッチされました# 5

結果はABC3です。と2同様に省略されます1


申し訳ありませんが、Exception( "1")は省略されていませんが、正常にキャッチされています
Black Maggie

@Black Maggieキャッシュされ、新しい例外がスローされます=>これはキャッシュされず、プログラムは終了します。そしてこのブロックの前にようやく実行されます。
nazar_art 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.