Ausnahme in catch und finally-Klausel geworfen

155

Auf eine Frage an Java an der Universität gab es diesen Codeausschnitt:

class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print(1);
            q();
        }
        catch (Exception i) {
            throw new MyExc2();
        }
        finally {
            System.out.print(2);
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            throw new MyExc1();
        }
        catch (Exception y) {
        }
        finally {
            System.out.print(3);
            throw new Exception();
        }
    }
}

Ich wurde gebeten, seine Ausgabe zu geben. Ich antwortete 13Exception in thread main MyExc2, aber die richtige Antwort ist 132Exception in thread main MyExc1. Warum ist es das? Ich kann einfach nicht verstehen, wohin es MyExc2geht.

Jubstuff
quelle

Antworten:

167

Wenn Sie Ihre Antwort lesen und sehen, wie Sie wahrscheinlich darauf gekommen sind, glauben Sie, dass eine "Ausnahme in Bearbeitung" "Vorrang" hat. Merken Sie sich:

Wenn eine neue Ausnahme in einen catch-Block oder schließlich einen Block ausgelöst wird, der sich aus diesem Block heraus ausbreitet, wird die aktuelle Ausnahme abgebrochen (und vergessen), wenn die neue Ausnahme nach außen weitergegeben wird. Die neue Ausnahme beginnt wie jede andere Ausnahme mit dem Abwickeln des Stapels, bricht aus dem aktuellen Block (dem Fang- oder Endblock) ab und unterliegt einem anwendbaren Fang oder Endblock auf dem Weg.

Beachten Sie, dass anwendbare Fang- oder Endblöcke Folgendes umfassen:

Wenn eine neue Ausnahme in einen catch-Block ausgelöst wird, unterliegt die neue Ausnahme weiterhin dem endgültigen Block dieses Catch, falls vorhanden.

throwVerfolgen Sie nun die Ausführung und denken Sie daran, dass Sie bei jedem Treffer die Verfolgung der aktuellen Ausnahme abbrechen und mit der Verfolgung der neuen Ausnahme beginnen sollten.

Bert F.
quelle
7
«Wenn Sie Ihre Antwort lesen und sehen, wie Sie wahrscheinlich darauf gekommen sind, glauben Sie, dass eine" Ausnahmebedingung "" Vorrang "hat.» Danke ... das war genau mein Gedanke :)
Jubstuff
39

Dies ist, was Wikipedia über die Klausel finally sagt:

Häufiger ist eine verwandte Klausel (endgültig oder sichergestellt), die ausgeführt wird, unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht, um normalerweise Ressourcen freizugeben, die im Hauptteil des Ausnahmebehandlungsblocks erfasst wurden.

Lassen Sie uns Ihr Programm analysieren.

try {
    System.out.print(1);
    q();
}

So 1wird der Ausgang in den Bildschirm sein, dann q()aufgerufen wird. In q()wird eine Ausnahme ausgelöst. Die Ausnahme wird dann abgefangen Exception y, tut aber nichts. Eine finally- Klausel wird dann ausgeführt (muss) und 3wird auf dem Bildschirm gedruckt. Da (in der Methode wird q()in der finally- Klausel eine Ausnahme ausgelöst , q()übergibt auch die Methode die Ausnahme an den übergeordneten Stapel (durch die throws Exceptionin der Methodendeklaration) new Exception()wird ausgelöst und abgefangen catch ( Exception i ), MyExc2wird eine Ausnahme ausgelöst (fügen Sie sie zunächst dem Ausnahmestapel hinzu ), aber ein final in the mainblock wird zuerst ausgeführt.

Also rein,

catch ( Exception i ) {
    throw( new MyExc2() );
} 
finally {
    System.out.print(2);
    throw( new MyExc1() );
}

Eine finally- Klausel heißt im Wesentlichen ... (denken Sie daran, wir haben sie gerade gefangen Exception iund geworfen MyExc2), 2wird auf dem Bildschirm gedruckt ... und nachdem die Klausel auf dem Bildschirm 2gedruckt wurde, wird eine MyExc1Ausnahme ausgelöst. MyExc1wird von der public static void main(...)Methode behandelt.

Ausgabe:

"132Exception in Thread main MyExc1"

Dozent ist richtig! :-)

Wenn Sie eine finally / catch-Klausel haben, wird im Wesentlichen eine finally- Klausel ausgeführt ( nachdem Sie die Ausnahme abgefangen haben, bevor Sie die abgefangene Ausnahme ausgelöst haben).

Buhake Sindi
quelle
Das catchwird ausgeführt, da q()ein Exceptionaus einem eigenen finallyBlock geworfen wurde.
Péter Török
"In q () wird eine Ausnahme ausgelöst, aber bevor die Ausnahme vollständig ausgelöst wird, wird zuerst eine finally-Klausel ausgeführt, sodass 3 auf dem Bildschirm gedruckt wird." Äh ... nein, die erste Ausnahme, die ausgelöst wird, qübergibt die Ausführung an die leerer catchBlock in q(der diese Ausnahme verschluckt), dann zum finallyBlock in q. Said blockiert schließlich Drucke 3und löst dann eine neue Ausnahme aus, die dank q's throws Exceptionden Stapel an das übergeordnete Element weitergibt.
Powerlord
38

Ausnahmen im finally-Block ersetzen Ausnahmen im catch-Block.

Zitat aus der Java Language Specification 14 Edition :

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).

Roland
quelle
21

Schließlich wird die Klausel auch dann ausgeführt, wenn eine Ausnahme von einer beliebigen Stelle im try / catch-Block ausgelöst wird.

Da es das letzte ist, das in ausgeführt wird, mainund eine Ausnahme ausgelöst wird, ist dies die Ausnahme, die die Anrufer sehen.

Daher ist es wichtig sicherzustellen, dass die finallyKlausel nichts auslöst, da sie Ausnahmen aus dem tryBlock verschlucken kann .

Alexander Pogrebnyak
quelle
5
Es wird auch AUCH ausgeführt, wenn im try / catch-Block keine Ausnahme ausgelöst wird
nanda
2
+1: Direkt und auf den Punkt, ohne sich über den gesamten Stapel zu schlängeln, den das OP bereits zu verstehen scheint.
Powerlord
9

A methodkann nicht throwzwei Ausnahmen gleichzeitig. Es wird immer der zuletzt geworfene geworfen exception, in diesem Fall immer der aus dem finallyBlock.

Wenn die erste Ausnahme von der Methode q()ausgelöst wird, wird sie abgefangen und dann von der endgültig blockierten Ausnahme ausgelöst.

q () -> geworfen new Exception -> main catch Exception -> throw new Exception -> finally einen neuen werfen exception(und der von der catchist "verloren")

Garis M Suero
quelle
3

Am einfachsten ist es, sich vorzustellen, dass die gesamte Anwendung eine globale Variable enthält, die die aktuelle Ausnahme enthält.

Exception currentException = null;

Wenn jede Ausnahme ausgelöst wird, wird "currentException" auf diese Ausnahme gesetzt. Wenn die Anwendung endet und currentException! = Null ist, meldet die Laufzeit den Fehler.

Außerdem werden die finally-Blöcke immer ausgeführt, bevor die Methode beendet wird. Sie können dann das Code-Snippet anfordern, um:

public class C1 {

    public static void main(String [] argv) throws Exception {
        try {
            System.out.print(1);
            q();

        }
        catch ( Exception i ) {
            // <-- currentException = Exception, as thrown by q()'s finally block
            throw( new MyExc2() ); // <-- currentException = MyExc2
        }
        finally {
             // <-- currentException = MyExc2, thrown from main()'s catch block
            System.out.print(2);
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }

    }  // <-- At application exit, currentException = MyExc1, from main()'s finally block. Java now dumps that to the console.

    static void q() throws Exception {
        try {
            throw( new MyExc1() ); // <-- currentException = MyExc1
        }
        catch( Exception y ) {
           // <-- currentException = null, because the exception is caught and not rethrown
        }
        finally {
            System.out.print(3);
            throw( new Exception() ); // <-- currentException = Exception
        }
    }
}

Die Reihenfolge, in der die Anwendung ausgeführt wird, lautet:

main()
{
  try
    q()
    {
      try
      catch
      finally
    }
  catch
  finally
}
CodingWithSpike
quelle
1

Es ist bekannt, dass der finally-Block nach dem try and catch ausgeführt wird und immer ausgeführt wird. Aber wie Sie gesehen haben, ist es manchmal etwas schwierig, sich das folgende Code-Snippet anzusehen, und Sie werden feststellen, dass die return- und throw-Anweisungen nicht funktionieren Tun Sie nicht immer das, was sie tun sollen, in der Reihenfolge, in der wir das Thema erwarten.

Prost.

/////////////Return dont always return///////

try{

    return "In Try";

}

finally{

    return "In Finally";

}

////////////////////////////////////////////


////////////////////////////////////////////    
while(true) { 

    try {

        return "In try";

   } 

   finally{

        break;     

    }          
}              
return "Out of try";      
///////////////////////////////////////////


///////////////////////////////////////////////////

while (true) {     

    try {            

        return "In try";    

     } 
     finally {   

         continue;  

     }                         
}
//////////////////////////////////////////////////

/////////////////Throw dont always throw/////////

try {

    throw new RuntimeException();

} 
finally {

    return "Ouuuups no throw!";

}
////////////////////////////////////////////////// 
Schlau
quelle
1
class MyExc1 extends Exception {}
class MyExc2 extends Exception {}
class MyExc3 extends MyExc2 {}

public class C1 {
    public static void main(String[] args) throws Exception {
        try {
            System.out.print("TryA L1\n");
            q();
            System.out.print("TryB L1\n");
        }
        catch (Exception i) {
            System.out.print("Catch L1\n");                
        }
        finally {
            System.out.print("Finally L1\n");
            throw new MyExc1();
        }
    }

    static void q() throws Exception {
        try {
            System.out.print("TryA L2\n");
            q2();
            System.out.print("TryB L2\n");
        }
        catch (Exception y) {
            System.out.print("Catch L2\n");
            throw new MyExc2();  
        }
        finally {
            System.out.print("Finally L2\n");
            throw new Exception();
        }
    }

    static void q2() throws Exception {
        throw new MyExc1();
    }
}

Auftrag:

TryA L1
TryA L2
Catch L2
Finally L2
Catch L1
Finally L1        
Exception in thread "main" MyExc1 at C1.main(C1.java:30)

https://www.compilejava.net/

Luiz Fernando
quelle
1
Während dieses Code-Snippet die Lösung sein kann, hilft das Hinzufügen einer Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Code-Vorschlag nicht kennen
Rahul Gupta
1

Die Logik ist klar, bis der Ausdruck abgeschlossen ist 13. Dann geworfen die Ausnahme in q()gefangen von catch (Exception i)in main()und new MyEx2()bereit ist , geworfen zu werden. Vor dem Auslösen der Ausnahme muss der finallyBlock jedoch zuerst ausgeführt werden. Dann wird die Ausgabe 132und finallyfordert auf, eine weitere Ausnahme auszulösen new MyEx1().

Da eine Methode nicht mehr als eine werfen kann Exception, wird immer die neueste ausgelöst Exception. Mit anderen Worten, wenn beide catchund finallyBlöcke versuchen zu werfen Exception, wird der ExceptionIn-Catch verschluckt und nur die Ausnahme in finallywird geworfen.

Daher wird in diesem Programm die Ausnahme MyEx2verschluckt und MyEx1ausgelöst. Diese Ausnahme wird verworfen main()und nicht mehr abgefangen, daher stoppt JVM und die endgültige Ausgabe ist 132Exception in thread main MyExc1.

Im Wesentlichen , wenn Sie eine haben finallyin einer try/catchKlausel, eine finallywird ausgeführt, nach dem Fang der Ausnahme , aber VOR jeder abgefangene Ausnahme zu werfen , und nur die neueste Ausnahme wäre am Ende geworfen werden .

yyFred
quelle
0

Ich denke, Sie müssen nur die finallyBlöcke gehen:

  1. Drucken Sie "1".
  2. finallyim qDruck "3".
  3. finallyim mainDruck "2".
Uwe Keim
quelle
0

Um diese Art von Situation zu behandeln, dh die durch finally block ausgelöste Ausnahme zu behandeln. Sie können den finally-Block mit try block umgeben: Sehen Sie sich das folgende Beispiel in Python an:

try:
   fh = open("testfile", "w")
   try:
      fh.write("This is my test file for exception handling!!")
   finally:
      print "Going to close the file"
      fh.close()
except IOError:
   print "Error: can\'t find file or read data"

quelle
-1

Ich denke das löst das Problem:

boolean allOk = false;
try{
  q();
  allOk = true;
} finally {
  try {
     is.close();
  } catch (Exception e) {
     if(allOk) {
       throw new SomeException(e);
     }
  }
}
Vouze
quelle
3
Welches Problem werden Sie "lösen"? Meinen Sie die Frage in der Prüfung? Nun, es hat bereits geantwortet. Wenn Sie das Problem des gegebenen Codes meinen, da es nur eine Prüfungsfrage ist, macht es keinen Sinn, es zu beschuldigen.
Earth Engine