Java-Ausnahme nicht abgefangen?

170

Ich habe ein kleines theoretisches Problem mit Try-Catch-Konstruktionen.

Ich habe gestern eine praktische Prüfung über Java abgelegt und verstehe folgendes Beispiel nicht:

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());
}

Die Frage war: "Wie wird die Ausgabe aussehen?"

Ich war mir ziemlich sicher, dass es AB2C3 sein würde, ABER Überraschung Überraschung, es ist nicht wahr.

Die richtige Antwort ist ABC3 (getestet und wirklich so).

Meine Frage ist, wo ist die Ausnahme ("2") geblieben?

Kousalik
quelle
8
+1 Ahh Mann, ich wusste diese Antwort. Das wurde ich in einem Interview gefragt. Es ist eine sehr gute Frage, um zu verstehen, wie try / catch / finally auf dem Stapel funktioniert.
Aber ich bin keine Wrapper-Klasse
10
Es gibt nur eine print-Anweisung, die eine Zahl drucken kann (die letzte :) print(e.getMessage()). Sie dachten, die Ausgabe wäre AB2C3: Haben Sie gedacht, der äußerste catchBlock würde zweimal ausgeführt?
Adrian Pronk
In Java wird der finally-Block ausgeführt, bevor eine Anweisung ausgeführt wird, mit der die Steuerung aus dem catch-Block übertragen wird, sofern er vorhanden ist. Wenn nur der Code im finally-Block die Steuerung nicht nach außen überträgt, wird der verzögerte Befehl vom catch-Block ausgeführt.
Thomas

Antworten:

198

Aus der Java-Sprachspezifikation 14.20.2. ::

Wenn der catch-Block aus Grund R abrupt abgeschlossen wird, wird der finally-Block ausgeführt. Dann gibt es eine Wahl:

  • Wenn der finally-Block normal abgeschlossen wird, wird die try-Anweisung aus Grund R abrupt abgeschlossen.

  • Wenn der finally-Block aus Grund S abrupt abgeschlossen wird, wird die try-Anweisung aus Grund S abrupt abgeschlossen (und Grund R wird verworfen) .

Wenn es also einen catch-Block gibt, der eine Ausnahme auslöst:

try {
    // ...
} catch (Exception e) {
    throw new Exception("2");
}

Es gibt aber auch einen finally-Block, der ebenfalls eine Ausnahme auslöst:

} finally {
    throw new Exception("3");
}

Exception("2")wird verworfen und nur weitergegeben Exception("3").

Adam Siemion
quelle
72
Dies gilt sogar für returnAussagen. Wenn Ihr finally-Block eine Rückgabe hat, überschreibt er jede Rückgabe in einem tryoder catch-Block. Aufgrund dieser "Funktionen" empfiehlt es sich, dass "finally block" niemals eine Ausnahme auslösen oder eine return-Anweisung haben sollte.
Augusto
Dies ist auch der Vorteil, den Try-with-Resources in Java 7 hat. Er behält die anfängliche Ausnahme bei, wenn beim Schließen von Ressourcen eine sekundäre Ausnahme generiert wird, was normalerweise das Debuggen erleichtert.
w25r
19

Im finally-Block ausgelöste Ausnahmen unterdrücken die zuvor im try- oder catch-Block ausgelöste Ausnahme.

Java 7-Beispiel: http://ideone.com/0YdeZo

Aus Javadocs Beispiel:


static String readFirstLineFromFileWithFinallyBlock(String path)
                                                     throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}

Wenn in diesem Beispiel die Methoden readLine und close beide Ausnahmen auslösen, löst die Methode readFirstLineFromFileWithFinallyBlock die vom finally-Block ausgelöste Ausnahme aus. Die vom try-Block ausgelöste Ausnahme wird unterdrückt.


Die neue try-withSyntax von Java 7 fügt einen weiteren Schritt der Ausnahmeunterdrückung hinzu: Ausnahmen, die im try-Block ausgelöst werden, unterdrücken diejenigen, die zuvor im try-with-Teil ausgelöst wurden.

aus demselben Beispiel:

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());
        }
    }

Aus dem Codeblock, der der Anweisung try-with-resources zugeordnet ist, kann eine Ausnahme ausgelöst werden. Im obigen Beispiel kann eine Ausnahme aus dem try-Block ausgelöst werden, und bis zu zwei Ausnahmen können aus der try-with-resources-Anweisung ausgelöst werden, wenn versucht wird, die Objekte ZipFile und BufferedWriter zu schließen. Wenn eine Ausnahme aus dem try-Block ausgelöst wird und eine oder mehrere Ausnahmen aus der try-with-resources-Anweisung ausgelöst werden, werden die aus der try-with-resources-Anweisung ausgelösten Ausnahmen unterdrückt, und die vom Block ausgelöste Ausnahme ist diejenige Dies wird von der Methode writeToFileZipFileContents ausgelöst. Sie können diese unterdrückten Ausnahmen abrufen, indem Sie die Throwable.getSuppressed-Methode aus der vom try-Block ausgelösten Ausnahme aufrufen.


In Code aus Frage verwirft jeder Block eindeutig die alte Ausnahme und protokolliert sie nicht einmal. Dies ist nicht gut, wenn Sie versuchen, einige Fehler zu beheben:

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

SD
quelle
9

Da throw new Exception("2");aus dem catchBlock geworfen wird und nicht try, wird es nicht wieder gefangen.
Siehe 14.20.2. Ausführung von try-finally und try-catch-finally .

Folgendes passiert:

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
}
Maroun
quelle
Ja, das stimmt, ich sehe, dass dies geschieht, aber ich suchte nach einer Erklärung - warum es sich so verhält
Kousalik
5

Ihre Frage ist sehr offensichtlich und die Antwort ist in gleichem Maße einfach. Das Ausnahmeobjekt mit der Nachricht "2" wird vom Ausnahmeobjekt mit der Nachricht "3" überschrieben.

Erläuterung: Wenn eine Ausnahme auftritt, wird das Objekt ausgelöst, um den zu behandelnden Block abzufangen. Wenn jedoch eine Ausnahme im catch-Block selbst auftritt, wird das Objekt zur Ausnahmebehandlung an den OUTER CATCH-Block (falls vorhanden) übertragen. Und das Gleiche geschah hier. Das Ausnahmeobjekt mit der Meldung "2" wird an den OUTER catch-Block übertragen. Aber warte .. Bevor du den inneren Try-Catch-Block verlässt, MUSS er ENDLICH AUSGEFÜHRT werden. Hier trat die Änderung auf, um die wir uns Sorgen machen. Ein neues EXCEPTION-Objekt (mit der Nachricht "3") wird verworfen oder dieser endgültige Block ersetzt das bereits ausgelöste Exception-Objekt (mit der Nachricht "2"). Als Ergebnis erhalten wir, wenn die Nachricht des Exception-Objekts gedruckt wird überschriebener Wert dh "3" und nicht "2".

Beachten Sie: Auf einem CATCH-Block kann nur ein Ausnahmeobjekt behandelt werden.

Bharat
quelle
2

Der finallyBlock läuft immer. Entweder Sie returnaus dem try-Block oder eine Ausnahme wird ausgelöst. Die im finallyBlock ausgelöste Ausnahme überschreibt die im catch-Zweig ausgelöste Ausnahme .

Darüber hinaus führt das Auslösen einer Ausnahme nicht zu einer eigenen Ausgabe. Die Zeile throw new Exception("2");schreibt nichts aus.

allprog
quelle
1
Ja, ich weiß, dass das Auslösen der Exception-Ausgabe nichts für sich ist, aber ich habe keinen Grund gesehen, warum Exception 2 gelöscht werden sollte. Ich bin wieder ein bisschen schlauer :-)
Kousalik
Es ist immer sehr lange und in sehr langer Zeit kann alles passieren ( siehe puzzle wouter.coekaerts.be/2012/puzzle-dreams )
Dainius
0

Nach Ihrem Code:

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());
}

Wie Sie hier sehen können:

  1. drucke A und löst eine Ausnahme aus # 1;
  2. Diese Ausnahme wurde durch catch-Anweisung und print B - # 2erfasst.
  3. Der Block wird schließlich # 3nach der Anweisung try-catch (oder nur try, wenn keine Ausnahme aufgetreten ist) ausgeführt und druckt C - # 4und löst eine neue Ausnahme aus.
  4. dieser hat durch externe catch-Anweisung gefangen # 5;

Ergebnis ist ABC3. Und 2wird auf die gleiche Weise weggelassen wie1

nazar_art
quelle
Entschuldigung, Ausnahme ("1") wird nicht ausgelassen, aber erfolgreich abgefangen
Black Maggie
@ Black Maggie Es wird zwischengespeichert und eine neue Ausnahme ausgelöst => Dies wird nicht zwischengespeichert und das Programm beendet. Und bevor dieser Block endlich ausgeführt wird.
Nazar_art