Ausnahme im Fangblock geworfen - wird sie wieder gefangen?

178

Dies mag wie eine Programmierfrage erscheinen, und ich hatte gedacht, ich wüsste die Antwort, aber jetzt muss ich sie noch einmal überprüfen. Wird in diesem Code unten die im ersten catch-Block ausgelöste Ausnahme vom allgemeinen Ausnahme-catch-Block unten abgefangen?

try {
  // Do something
} catch(IOException e) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}

Ich dachte immer, die Antwort wäre nein, aber jetzt habe ich ein merkwürdiges Verhalten, das dadurch verursacht werden könnte. Die Antwort ist wahrscheinlich für die meisten Sprachen dieselbe, aber ich arbeite in Java.

roryf
quelle
2
Vielleicht könnten Sie das "merkwürdige Verhalten" beschreiben?
Jeffrey L Whitledge
Sind Sie sicher, dass die ApplicationException nicht an anderer Stelle ausgelöst wird und sich bis zu diesem Block ausbreitet?
sblundy
Ich habe dies in meiner Eclipse-IDE bemerkt. Es ist anstrengend, mich zu zwingen, die "neue Ausnahme werfen" in einen Versuchsblock zu setzen, aber ich weiß nicht warum. Ich habe es in der Vergangenheit getan, ohne das zu tun. Ich verstehe nicht, warum ein Try-Block erforderlich wäre. Viele Beispiele bei Google zeigen, dass Personen keinen Try-Block benötigen. Liegt es daran, dass ich eine if-Anweisung einsetze?
Djangofan

Antworten:

212

Nein, da das neue thrownicht trydirekt im Block ist.

Chris Jester-Young
quelle
Nehmen wir an, der Code lautet wie folgt: try {} catch (Ausnahme e) {System.err.println ("In catch Exception:" + e.getClass ()); } catch (IOException e) {System.err.println ("In catch IOException:" + e.getClass ()); } und der Code im try-Block generiert eine E / A-Ausnahme. Geht er zum unmittelbaren allgemeinen Ausnahmeblock oder fliegt er zum IOException-catch-Block?
sofs1
4
@ user3705478 Der Code wird nicht kompiliert, um solche fehlerhaften Situationen zu vermeiden. Im Allgemeinen dürfen Sie keinen Fang für eine Unterklasse nach einem Fang ihrer Oberklasse im selben try-Block haben.
Chris Jester-Young
Vielen Dank. Ich bekomme also einen Fehler bei der Kompilierung, oder? Ich werde es testen, wenn ich nach Hause komme.
sofs1
Das hängt von @ user3705478 ab. Wenn a RuntimeExceptionaus dem catchBlock geworfen wird, tritt kein Kompilierungsfehler auf.
Randy the Dev
@AndrewDunn Ich glaube nicht, dass es um die Frage von user3705478 geht, sondern darum, was passiert, wenn eine übergeordnete Ausnahmeklausel vor einer untergeordneten Ausnahmeklausel catchaufgeführt wird . Ich verstehe, dass Java dies nicht zulässt und es beim Kompilieren abgefangen wird. catch
Chris Jester-Young
70

Nein, es ist sehr einfach zu überprüfen.

public class Catch {
    public static void main(String[] args) {
        try {
            throw new java.io.IOException();
        } catch (java.io.IOException exc) {
            System.err.println("In catch IOException: "+exc.getClass());
            throw new RuntimeException();
        } catch (Exception exc) {
            System.err.println("In catch Exception: "+exc.getClass());
        } finally {
            System.err.println("In finally");
        }
    }
}

Sollte drucken:

In catch IOException: Klasse java.io.IOException
Endlich
Ausnahme im Thread "main" java.lang.RuntimeException
        bei Catch.main (Catch.java:8)

Technisch gesehen könnte dies ein Compiler-Fehler, ein implementierungsabhängiges, nicht angegebenes Verhalten oder etwas anderes sein. Das JLS ist jedoch ziemlich gut festgenagelt und die Compiler sind gut genug für diese Art von einfachen Dingen (Generics Corner Case kann eine andere Sache sein).

Beachten Sie auch, dass beim Kompilieren der beiden Fangblöcke keine Kompilierung erfolgt. Der zweite Fang wäre völlig unerreichbar.

Beachten Sie, dass der finally-Block immer ausgeführt wird, auch wenn ein catch-Block ausgeführt wird (abgesehen von dummen Fällen wie Endlosschleifen, Anhängen über die Werkzeugschnittstelle und Beenden des Threads, Umschreiben des Bytecodes usw.).

Tom Hawtin - Tackline
quelle
3
Der naheliegendste Weg, a zu vermeiden, finallyist natürlich anzurufen System.exit. :-P
Chris Jester-Young
3
@Chris Jester-Young for(;;);ist kürzer, in der Sprache enthalten, führt nicht viel zu Nebenwirkungen und ist für mich offensichtlicher.
Tom Hawtin - Tackline
1
System.exitist CPU-freundlicher! : -O Aber ja, okay, das ist eindeutig ein subjektives Kriterium. Außerdem wusste ich nicht, dass Sie ein Code-Golfer sind. ;-)
Chris Jester-Young
27

In der Java-Sprachspezifikation heißt es in Abschnitt 14.19.1:

Wenn die Ausführung des try-Blocks aufgrund eines Wurfs eines Werts V abrupt abgeschlossen wird, gibt es eine Auswahl:

  • Wenn der Laufzeittyp von V dem Parameter einer catch-Klausel der try-Anweisung zugewiesen werden kann, wird die erste (am weitesten links stehende) solche catch-Klausel ausgewählt. Der Wert V wird dem Parameter der ausgewählten catch-Klausel zugewiesen, und der Block dieser catch-Klausel wird ausgeführt. Wenn dieser Block normal abgeschlossen wird, wird die try-Anweisung normal abgeschlossen. Wenn dieser Block aus irgendeinem Grund abrupt abgeschlossen wird, wird die try-Anweisung aus demselben Grund abrupt abgeschlossen.

Referenz: http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#24134

Mit anderen Worten, der erste einschließende Fang, der die Ausnahme behandeln kann, funktioniert, und wenn eine Ausnahme aus diesem Fang herausgeworfen wird, liegt dies nicht im Umfang eines anderen Fangs für den ursprünglichen Versuch, sodass sie nicht versuchen, damit umzugehen.

Eine verwandte und verwirrende Sache zu wissen ist, dass in einer try- [catch] -finally-Struktur ein finally-Block eine Ausnahme auslösen kann und in diesem Fall jede vom try- oder catch-Block ausgelöste Ausnahme verloren geht. Das kann verwirrend sein, wenn Sie es zum ersten Mal sehen.

Alex Miller
quelle
Ich wollte nur hinzufügen, dass Sie seit Java 7 die Verwendung von try-with-resources vermeiden können. Wenn dann tryUND finallybeide werfen, finallywird unterdrückt, aber auch zur Ausnahme von HINZUGEFÜGT try. Wenn Sie ebenfalls catchwerfen, haben Sie kein Glück, es sei denn, Sie erledigen das selbst über 'addSuppressed' und fügen die tryAusnahme hinzu - dann haben Sie alle drei.
LAFK sagt Reinstate Monica
6

Wenn Sie eine Ausnahme aus dem catch-Block auslösen möchten, müssen Sie Ihre Methode / Klasse / etc. dass es diese Ausnahme werfen muss. Wie so:

public void doStuff() throws MyException {
    try {
        //Stuff
    } catch(StuffException e) {
        throw new MyException();
    }
}

Und jetzt wird dein Compiler dich nicht anschreien :)

Mastergeek
quelle
4

Nein - Wie Chris Jester-Young sagte, wird es bis zum nächsten Versuch in der Hierarchie geworfen.

Ian P.
quelle
2

Wie oben erwähnt ...
Ich möchte hinzufügen, dass Sie, wenn Sie Probleme haben, die Vorgänge zu sehen, das Trace im Debugger nicht reproduzieren können, eine Ablaufverfolgung hinzufügen können, bevor Sie die neue Ausnahme erneut auslösen (mit dem guten alten System) .out.println schlimmer noch, mit einem guten Protokollsystem wie log4j sonst).

PhiLho
quelle
2

Es wird nicht vom zweiten Fangblock erfasst. Jede Ausnahme wird nur in einem Try-Block abgefangen. Sie können jedoch Versuche verschachteln (nicht, dass dies im Allgemeinen eine gute Idee ist):

try {
    doSomething();
} catch (IOException) {
   try {
       doSomething();
   } catch (IOException e) {
       throw new ApplicationException("Failed twice at doSomething" +
       e.toString());
   }          
} catch (Exception e) {
}
Vinko Vrsalovic
quelle
Ich habe einen ähnlichen Code geschrieben. Aber ich bin nicht überzeugt. Ich möchte einen Thread.sleep () in meinem catch-Block aufrufen. Thread.sleep löst jedoch eine InterruptedException aus. Ist es richtig (eine bewährte Methode), dies so zu tun, wie Sie es in Ihrem Beispiel gezeigt haben?
Riroo
1

Nein, da sich alle Catches auf denselben Try-Block beziehen, wird das Werfen aus einem Catch-Block heraus von einem umschließenden Try-Block abgefangen (wahrscheinlich in der Methode, die diesen aufgerufen hat).

Uri
quelle
-3

Alter Beitrag, aber "e" -Variable muss eindeutig sein:

try {
  // Do something
} catch(IOException ioE) {
  throw new ApplicationException("Problem connecting to server");
} catch(Exception e) {
  // Will the ApplicationException be caught here?
}
Ted K.
quelle
5
Dies sollte ein Kommentar und keine Antwort sein. Es bietet nichts, um die eigentliche Frage zu beantworten - es ist nur eine Kritik der OP-Frage.
Derek W