web-dev-qa-db-ja.com

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

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")はどこに行きましたか?

169
Kousalik

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")のみが伝播されます。

196
Adam Siemion

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メソッドを呼び出すことで、これらの抑制された例外を取得できます。


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

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

19
S.D.

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
}
9
Maroun

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

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

Keep Remember:CATCHブロックで処理できる例外オブジェクトは1つだけです。

5
Bharat

finallyブロックは常に実行されます。 tryブロック内からreturnを実行するか、例外がスローされます。 finallyブロックでスローされた例外は、catchブランチでスローされた例外をオーバーライドします。

また、例外をスローしても、それ自体では出力が発生しません。行throw new Exception("2");は何も書き出しません。

2
allprog

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

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

結果はABC3です。 2は、1と同じ方法で省略されます

0
nazar_art