Benötigen Sie wirklich den "Endlich" -Block?

72

Es gibt 3 Permutationen eines Versuchs ... fangen ... endlich in Java blockieren.

  1. versuche ... zu fangen
  2. versuche ... endlich zu fangen
  3. versuche ... endlich

Sobald der finally-Block ausgeführt wurde, geht die Steuerung zur nächsten Zeile nach dem finally-Block. Wenn ich den finally-Block entferne und alle seine Anweisungen nach dem try ... catch-Block in die Zeile verschiebe, hat dies den gleichen Effekt wie sie im finally-Block?

Amit Kumar Gupta
quelle
3
Diese Frage wurde vor stackoverflow.com/questions/3354823/how-to-use-finally beantwortet . Es ist also ein Duplikat
Shervin Asgari
@Shervin, haben Sie beide Fragen gelesen, bevor Sie sie als Duplikat markiert haben
Amit Kumar Gupta
Mögliches Duplikat der endgültigen Verwendung
Tony Danilov

Antworten:

51

Ich denke, Willenscode kommt dem Ausdruck des Schlüsselpunkts hier am nächsten, und wahrscheinlich meint es jeder, ist aber nicht klar.

Das Problem ist, dass tatsächlich etwas sehr falsch mit dem ist, was Sie fragen: "Wenn ich alle Anweisungen nach dem catch-Block schreibe, anstatt sie in den finally-Block zu schreiben, dann stimmt dann etwas nicht?"

Wenn Sie alle Anweisungen nach dem catch-Block schreiben, implizieren Sie dies

1) Sie werden immer die Ausnahme abfangen.

2) Sie fahren immer mit den nächsten Anweisungen fort, nachdem Sie die Ausnahme abgefangen haben.

Dies bedeutet, dass Sie die Ausführung nach einer Ausnahme immer "normal" fortsetzen, was im Allgemeinen etwas ist, was Sie eigentlich nie tun möchten.

Ausnahmen sollten genau das sein - außergewöhnlich. Wenn Sie tatsächlich eine Ausnahme behandeln können, ist es immer besser, Ihren Code zu schreiben, um diese Bedingungen zuerst zu berücksichtigen und überhaupt nicht zu einer Ausnahme zu führen. Wenn Sie diesem Modell folgen, sind Ausnahmen wirklich außergewöhnlich - Bedingungen, die Sie nicht vorhersehen oder höchstens nicht beheben konnten. Wirklich nicht vorwegzunehmen ist, worauf Sie hinarbeiten sollten. Dies bedeutet im Allgemeinen, dass Sie keine echten Ausnahmen verarbeiten können. Dies bedeutet auch, dass Sie die Ausführung nicht einfach fortsetzen sollten, sondern häufig die Anwendung beenden.

Normalerweise wird zugelassen, dass ein Fehler den Aufrufstapel wieder weitergibt. Einige sagen, dass dies mit der Möglichkeit geschieht, dass jemand weiter oben in der Kette damit umgehen kann. Ich würde sagen, dass im Wesentlichen nie passiert, es gibt zwei wirkliche Zwecke, um dies zu tun. Zum einen kann der Benutzer dies beheben, falls es eines gibt. Sie geben den Fehler also wieder weiter, bis Sie dort sind, wo Sie ihn dem Benutzer melden können. Oder zweitens kann ein Benutzer das Problem nicht beheben, aber Sie möchten den gesamten Aufrufstapel zum Debuggen abrufen. Dann fangen Sie es oben, um anmutig zu scheitern.

Der finally-Block sollte jetzt mehr Bedeutung für Sie haben. Wie jeder sagt, läuft es immer. Die klarste Verwendung eines Endlich ist wirklich ein Versuch ... Endlich zu blockieren. Was Sie jetzt sagen, ist, wenn der Code gut läuft, großartig. Wir müssen noch etwas aufräumen und das wird schließlich immer ausgeführt, dann gehen wir weiter. Aber wenn eine Ausnahme auftritt, brauchen wir diesen endgültigen Block jetzt wirklich, da wir möglicherweise noch einige Aufräumarbeiten durchführen müssen, aber wir fangen die Ausnahme hier nicht mehr ab, sodass wir nicht mehr weitermachen werden. Der endgültige Block ist wichtig, um sicherzustellen, dass die Bereinigung erfolgt.

Die Idee einer Ausnahme, die die Ausführung immer stoppt, kann für jemanden schwer zu verstehen sein, bis er eine gewisse Erfahrung hat, aber das ist in der Tat die Art und Weise, Dinge immer zu tun. Wenn ein Fehler aufgetreten ist, war er entweder so geringfügig, dass Sie ihn zunächst hätten berücksichtigen müssen, oder es warten immer mehr Fehler auf Sie.

Fehler "schlucken" - sie zu fangen und weiterzumachen ist das Schlimmste, was Sie tun können, weil Ihr Programm unvorhersehbar wird und Sie keine Fehler finden und beheben können.

Gut geschriebener Code enthält so viele try ... finally-Blöcke wie nötig, um sicherzustellen, dass Ressourcen unabhängig vom Ergebnis immer freigegeben werden. Gut geschriebener Code enthält jedoch im Allgemeinen nur eine geringe Anzahl von try ... catch-Blöcken, die in erster Linie vorhanden sind, damit eine Anwendung so ordnungsgemäß wie möglich fehlschlägt oder an den Benutzer zurückgestellt wird, was bedeutet, dass zumindest immer eine Nachricht an den Benutzer usw. weitergeleitet wird. Aber normalerweise fängt man nicht nur einen Fehler und macht weiter.

Sisyphus
quelle
9
Ihre Schlussfolgerung (1 und 2) ist absolut falsch - Nein, diese Frage bedeutet nicht, dass er immer eine Ausnahme fängt und / oder immer mit konsequenten Anweisungen fortfährt. 1) Auch wenn Sie keine Ausnahme abfangen, fahren Sie mit den folgenden Zeilen fort, es sei denn, Sie kehren von der Funktion zurück. 2) Wenn Sie nicht zurückkehren, egal ob Sie mit try block oder catch fortfahren - Sie werden IMMER mit den nachfolgenden Anweisungen fortfahren (falls vorhanden), und das ist richtig, hier ist nichts falsch. Ich hätte geantwortet, dass man beim Versuch zurückkehren kann - aber schließlich noch ausgeführt wird. Das ist der Unterschied.
Giorgi Tsiklauri
@GiorgiTsiklauri Sie sagen, dass die Ausführung fortgesetzt wird "Auch wenn Sie keine Ausnahme abfangen", aber dies ist nicht immer wahr - Meinen Sie, dass überhaupt keine Ausnahme auftritt? Wenn ja, berücksichtigen Sie nicht, wenn es eine Ausnahme gibt, aber es ist keine, für die Sie eine catch-Anweisung haben. Endlich würde das gut funktionieren. "Rückkehr während des Versuchs" ist definitiv nicht der einzige Grund, der schließlich existiert.
Poikilos
83

Ich weiß, dass dies eine sehr alte Frage ist, aber ich bin heute auf sie gestoßen und war durch die gegebenen Antworten verwirrt. Ich meine, sie sind alle richtig, aber alle antworten auf theoretischer oder sogar philosophischer Ebene, wenn es eine sehr einfache praktische Antwort auf diese Frage gibt.

Wenn Sie ein return-, break-, continue- oder ein anderes Java-Schlüsselwort eingeben, das die sequentielle Ausführung von Code im catch-Block ändert (oder sogar den try-Block) , werden die Anweisungen im finally-Block weiterhin ausgeführt.

Zum Beispiel:

public void myFunc() {

    double p = 1.0D;
    String str = "bla";
    try{
        p = Double.valueOf(str);
    }
    catch(Exception ex){
        System.out.println("Exception Happened");
        return;  //return statement here!!!
    }finally{
        System.out.println("Finally");
    }
    System.out.println("After finally");
}

Bei Ausführung wird dieser Code gedruckt:

Exception Happened 
Finally

Das ist der wichtigste Grund für die Existenz eines endgültigen Blocks. Die meisten Antworten implizieren es oder beziehen sich auf es am Rande, aber keine von ihnen betont es. Ich denke, weil dies eine Art Neuling-Frage ist, ist eine so einfache Antwort sehr wichtig.

Angelos Makrygiorgos
quelle
4
Danke, das war die Antwort, nach der ich gesucht habe.
Madhavi
43

Das Highlight ist, dass ein finallyBlock garantiert ausgeführt wird, auch wenn eine Ausnahme ausgelöst und nicht abgefangen wird . Anschließend verwenden Sie den finallyBlock als einmalige Chance, um die erforderlichen Bereinigungen wie das Schließen von Streams durchzuführen. Der Code nach dem finally-Block wird möglicherweise nie erreicht.

Aus dem Java- Tutorial

Der finally-Block wird immer ausgeführt, wenn der try-Block beendet wird. Dadurch wird sichergestellt, dass der finally-Block auch dann ausgeführt wird, wenn eine unerwartete Ausnahme auftritt. Aber schließlich ist es nicht nur für die Ausnahmebehandlung nützlich - es ermöglicht dem Programmierer zu vermeiden, dass der Bereinigungscode versehentlich durch eine Rückgabe umgangen, fortgesetzt oder unterbrochen wird. Das Einfügen von Bereinigungscode in einen finally-Block ist immer eine gute Vorgehensweise, auch wenn keine Ausnahmen zu erwarten sind.

willcodejavaforfood
quelle
1
@articlestack Denken Sie auch daran, dass finally-Block nicht aufgerufen wird, wenn System.exit (); wird genannt.
Toter Programmierer
1
@ Suresh S: guter Punkt, aber trotzdem im Fall von System.exit ... nichts ist wichtig ... (Ressourcen werden auf Betriebssystemebene freigegeben)
Helios
@helios Sie können nicht verallgemeinern, insbesondere wenn der Prozess Ressourcen eines anderen Prozesses verwendet.
Rubenjohn
1
Wird schließlich ausgeführt, auch wenn eine Ausnahme aus dem catch-Block ausgelöst wird?
Rubenjohn
10

Wenn ich die Frage verstehe, fragen Sie, was der Unterschied ist zwischen:

try {
    Foo f = new Foo();
    f.bar();
}
finally
{
    Foo.baz();
}

Und:

// This doesn't actually compile because a try block needs a catch and/or finally block
try {
    Foo f = new Foo();
    f.bar();
}
Foo.baz();

Oder eher:

Foo f = new Foo();
f.bar();
Foo.baz();

Der Unterschied besteht darin, dass der Block im ersten Fall ausgeführt wird, wenn eine new Foo()oder f.bar()eine Ausnahme finallyausgelöst wird, Foo.baz()in den letzten beiden Fällen jedoch nicht: Stattdessen wird die Steuerung übersprungen, Foo.baz()während die JVM nach einem Ausnahmebehandler sucht.


BEARBEITEN

Wenn Sie auf Ihren Kommentar antworten, was ist mit:

Foo f = new Foo();
try {
    f.bar();
}
catch (Exception ex)
{
    // ...
}

f.baz();

Sie haben Recht, wenn der catchBlock die Ausnahme nicht erneut auslöst oder von der Methode zurückkehrt, die angibt, dass ein Fehler aufgetreten ist, und dann f.baz()aufgerufen wird, unabhängig davon, ob eine Ausnahme aufgetreten ist . Selbst in diesem Fall finallydient der Block jedoch als Dokumentation, f.baz()die zur Bereinigung verwendet wird.

Noch wichtiger ist, dass die Tatsache, dass eine Ausnahme ausgelöst wurde, normalerweise wichtig ist. Daher ist es sehr schwierig, Code zu schreiben, der weiterhin das tut, was er getan hat, ohne zu wissen, dass eine Ausnahme ausgelöst wurde. Es gibt Zeiten, in denen Ausnahmen auf dumme Dinge hinweisen, die Sie ignorieren können, und in diesem Fall sollten Sie die Ausnahme schlucken. In den meisten Fällen möchten Sie jedoch einen Fehler signalisieren, indem Sie die Ausnahme erneut auslösen (oder eine andere Ausnahme auslösen) oder mit einem Fehlercode von der Methode zurückkehren.

Wenn beispielsweise f.bar()a Stringin a konvertiert werden soll Doubleund bei einem Fehler a ausgelöst wird NumberFormatException, muss der Code nach dem tryBlock wahrscheinlich wissen, dass der Stringnicht tatsächlich in a konvertiert wurde Double. Im Allgemeinen möchten Sie nach dem catchBlockieren nicht fortfahren . Stattdessen möchten Sie einen Fehler signalisieren. Dies wird als "Abbruch bei Fehler" bezeichnet (im Vergleich zu "Wiederaufnahme bei Fehler", was wahrscheinlich als "Durcheinander nach einem Fehler mit gekreuzten Fingern" bezeichnet werden sollte).

Außer in besonderen Fällen können Sie sich verwirren. Zum Beispiel kann der catchkönnte Block gesetzt die relevant Doublezu , Double.NaNdie speziell zu propagieren Fehler richtig in mathematischen Ausdrücken ausgelegt ist. Selbst in diesem Fall finallydient der Block als Dokumentation, f.baz()die an einer Art Bereinigung beteiligt ist.

Max Lybbert
quelle
1
Ich sagte, dass Sie versuchen, beide Blöcke zu fangen. Was ist nun endlich nötig? Denn wenn ein Fehler abgefangen wurde und bestimmte Maßnahmen ergriffen wurden, muss die nächste Anweisung (entweder endlich {} oder die Anweisung nach catch {}) ausgeführt werden.
Amit Kumar Gupta
@aticlestak: Ich habe den Artikel bearbeitet, um hoffentlich Ihre Frage zu beantworten. Der von Ihnen beschriebene Fall aktiviert die Philosophie zur Verwendung von Ausnahmen.
Max Lybbert
3

Der finally-Block enthält Codezeilen, die ausgeführt werden sollen, unabhängig davon, ob eine Ausnahme abgefangen wurde oder nicht. Auch wenn Sie sich entscheiden, die Ausführung von Code in dieser Methode zu beenden. Code nach dem tcf wird möglicherweise nicht ausgeführt, aber der endgültige Code ist "garantiert" (garantiert im Sinne eines nicht abstürzenden, sofort brechenden, nicht handhabbaren Fehlers).

Sascha
quelle
Was ist, wenn versuchen ... fangen ist da. Aber ich schreibe Restanweisungen ohne endgültig zu blockieren oder endgültig zu blockieren? Was ist der Unterschied?
Amit Kumar Gupta
2

Ja, da wäre etwas sehr kritisches falsch.

Und das heißt, Ihr Code würde nur ausgeführt, wenn ein Fehler vorliegt .

Anweisungen im Inneren werden finallyimmer ausgeführt, unabhängig davon, ob eine Ausnahme ausgelöst wird. Das ist der Punkt.

Mittags Seide
quelle
2
Bereinigungscode, den Sie beispielsweise ausführen möchten, unabhängig davon, ob ein Fehler vorliegt oder nicht. Das gehört in einen Endblock.
Michael Petrotta
Sie können sicherstellen, dass jedes Mal Fehler auftreten, indem Sie Throw new Exception () hinzufügen. als Ihre letzte Zeile im Try-Block LOL
JumpingJezza
2
Ich denke, Sie haben die "alle Anweisungen nach dem Catch-Block" falsch verstanden. Für mich klingt es wie try {...; } Fang { ....; } alle Anweisungen ;
Maxem
@JumpingJezza: Ja, aber diese Aussage ist verwirrt, weil der Code in a schließlich nicht nativ in einem try-Block enthalten ist, sodass Fehler dort genauso wie überall abgefangen werden müssen. Ihr Code in a finally sollte robust sein, damit er aktiv keine Ausnahmen generiert.
Mittagsseide
@ Maxem: Das ist eine Interpretation, aber ich kann mir nicht vorstellen, warum ich sie fragen soll, da es so bizarr wäre, davon verwirrt zu sein ...
Noon Silk
2

Schließlich Block speziell zum Zeitpunkt der Ausnahmeverhütung verwendet. Wenn ein Laufzeitfehler auftritt, kann das Programm zum Beenden führen. Zu diesem Zeitpunkt wird also finally block aufgerufen, bevor das Programm beendet wird. Normalerweise enthält 'finally' Anweisungen zum Schließen von Verbindungen, Speichern von Vorgängen und Eingaben von Dateien sowie Ausgeben von Vorgängen zum Schließen von Verbindungen.

janasainik
quelle
1

Wenn Ihr Code niemals eine Ausnahme auslöst oder Sie alle Ausnahmen verbrauchen, ist dies korrekt. Das passiert nicht immer.

fastcodejava
quelle