web-dev-qa-db-de.com

Brauchen Sie wirklich den "endlich" -Block

Es gibt 3 Permutationen eines try ... catch ... endlich in Java blockieren.

  1. versuche es ... fangen
  2. versuche es ... fangen ... endlich
  3. versuche es ... endlich

Sobald der finally-Block ausgeführt wird, 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 schiebe, hätte das dann den gleichen Effekt, als hätte er den finally-Block?

62

Ich denke, Willcode kommt dem Schlüsselpunkt hier am nächsten, und wahrscheinlich meint jeder, aber es ist nicht klar.

Das Problem ist, dass tatsächlich etwas sehr falsch mit dem ist, was Sie fragen: "Wenn ich alle Anweisungen nach catch-Block schreibe, anstatt sie in final-block zu schreiben, dann wäre da etwas falsch?" 

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

1) Sie werden immer die Ausnahme fangen.

2) Wenn Sie die Ausnahme abfangen, werden Sie immer mit den nächsten Anweisungen fortfahren.

Dies bedeutet, dass Sie die Ausführung nach einer Ausnahme immer "normal" fortsetzen. Dies ist im Allgemeinen etwas, das Sie never tatsächlich tun möchten.

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

Normalerweise wird zugelassen, dass ein Fehler den Call-Stack weitervermittelt. Einige sagen, dies sei bei der unwahrscheinlichen Möglichkeit der Fall, dass jemand in der oberen Reihe der Kette damit umgehen könnte. Ich würde sagen, dass im Wesentlichen nie etwas passiert, es gibt zwei wirkliche Gründe, dies zu tun. Es kann sich um etwas handeln, das der Benutzer beheben kann, falls vorhanden. So verbreiten Sie den Fehler so lange, bis Sie an den Ort gelangen, an dem Sie ihn dem Benutzer melden können. Oder zweitens, ein Benutzer kann das Problem nicht beheben, aber Sie möchten den gesamten Aufrufstack für das Debugging abrufen. Dann fangen Sie es oben, um graziös zu versagen.

Der finally-Block sollte jetzt mehr Bedeutung für Sie haben. Wie jeder sagt, läuft es immer. Die klarste Verwendung eines Endes ist wirklich in einem Versuch ... Endlich Block. Was Sie jetzt sagen, ist, wenn der Code gut läuft, großartig. Wir müssen immer noch ein paar Aufräumarbeiten durchführen und das wird schließlich immer ausgeführt, dann gehen wir weiter. Wenn jedoch eine Ausnahmebedingung auftritt, brauchen wir jetzt wirklich die endgültige Blockierung, weil wir möglicherweise noch ein paar Aufräumarbeiten durchführen müssen. Die Ausnahme wird hier jedoch nicht mehr erfasst, sodass wir uns nicht mehr weiter bewegen werden. Der finally-Block ist wichtig, um sicherzustellen, dass die Reinigung erfolgt.

Die Vorstellung, dass eine Ausnahme immer die Ausführung anhält, kann für jemanden schwer zu verstehen sein, bis sie über eine gewisse Erfahrung verfügt, aber das ist tatsächlich der Weg, immer etwas zu tun. Wenn ein Fehler aufgetreten ist, war er entweder so geringfügig, dass Sie ihn für den Anfang berücksichtigt haben sollten, oder es gibt immer mehr Fehler, die auf der ganzen Linie warten.

"Schlucken" von Fehlern - das Fangen und Fortfahren 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 dies erforderlich ist, um sicherzustellen, dass Ressourcen unabhängig vom Ergebnis immer freigegeben werden. Gut geschriebener Code enthält jedoch im Allgemeinen nur eine kleine Anzahl von try ... catch-Blöcken, die in erster Linie dazu dienen, dass eine Anwendung so fehlerfrei wie möglich ausfällt oder auf den Benutzer verschoben wird, was bedeutet, dass dem Benutzer zumindest immer eine Nachricht übergeben wird. Normalerweise fängt man nicht einfach einen Fehler auf und macht weiter.

45
Sisyphus

Ich weiß, dass dies eine sehr alte Frage ist, aber ich kam heute rüber und war verwirrt von den gegebenen Antworten. Ich meine, sie sind alle korrekt, 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 des Codes im catch-Block (oder sogar try-Block) ändert, 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");
}

wenn er ausgeführt wird, wird dieser Code gedruckt:

Exception Happened 
Finally

Das ist der wichtigste Grund für das Bestehen eines endgültigen Blocks. Die meisten Antworten deuten darauf hin oder beziehen sich darauf an der Seitenlinie, aber keine von ihnen betont es. Ich denke, da dies eine Art Neulinge-Frage ist, ist eine solche einfache Antwort sehr wichtig.

49

Das Highlight ist, dass die Ausführung eines finally-Blocks garantiert ist, auch wenn eine Ausnahme ausgelöst wird und nicht abgefangen wird. Sie verwenden dann den finally-Block als einmalige Gelegenheit, um die erforderlichen Aufräumarbeiten 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 wird beendet. Dies stellt sicher, dass Der finally-Block wird auch ausgeführt, wenn Eine unerwartete Ausnahme tritt auf. Aber Schließlich ist es nützlich für mehr als nur Ausnahmebehandlung - ermöglicht die Programmierer, um eine Bereinigung zu vermeiden Code, der versehentlich von einem .__ umgangen wird. zurückkehren, fortfahren oder brechen. Putten Bereinigungscode in einem finally-Block lautet immer eine gute Praxis, auch wenn nein Ausnahmen werden erwartet.

39

Wenn ich die Frage verstehe, fragt man sich, 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 ist, dass, wenn entweder new Foo() oder f.bar() eine Ausnahme auslösen, der finally-Block im ersten Fall ausgeführt wird, dass jedoch Foo.baz() in den letzten beiden Fällen nicht ausgeführt wird: Stattdessen überspringt die Steuerung Foo.baz(), während die JVM nach einem Ausnahmebehandler.


EDIT

Antworten Sie auf Ihren Kommentar:

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

f.baz();

Sie haben Recht, wenn der catch-Block die Ausnahme nicht erneut auslöst oder von der Methode zurückkehrt, dass ein Fehler aufgetreten ist, wird f.baz() aufgerufen, unabhängig davon, ob eine Ausnahme vorliegt. In diesem Fall dient der finally-Block jedoch als Dokumentation dafür, dass f.baz() für die Bereinigung verwendet wird.

Noch wichtiger ist die Tatsache, dass normalerweise eine Ausnahme ausgelöst wurde. Daher ist es sehr schwierig, Code zu schreiben, der weiterhin das macht, was er gerade macht, ohne zu wissen, dass eine Ausnahme ausgelöst wurde. Es gibt Zeiten, in denen Ausnahmen auf dumme Dinge hinweisen, die Sie ignorieren können. In diesem Fall sollten Sie die Ausnahme schlucken. Häufiger werden Sie jedoch einen Fehler signalisieren wollen, indem Sie entweder die Ausnahme erneut auslösen (oder eine andere Ausnahme auslösen) oder von der Methode mit einem Fehlercode zurückkehren.

Wenn beispielsweise f.bar() eine String in eine Double konvertieren soll und bei einem Fehler eine NumberFormatException auslöst, muss der Code nach dem try-Block wahrscheinlich wissen, dass die String nicht tatsächlich in eine Double konvertiert wurde. Daher möchten Sie im Allgemeinen nicht nach dem catch-Block fortfahren. Stattdessen möchten Sie einen Fehler signalisieren. Dies wird als "Abbruch bei Versagen" bezeichnet (im Vergleich zu "Wiederaufnahme bei Versagen", was wahrscheinlich als "Verwirrung nach Versagen mit gekreuzten Fingern" bezeichnet werden sollte).

Ausnahmsweise können Sie sich in besonderen Fällen durcheinander bringen. Zum Beispiel könnte der catch-Block die relevante Double auf Double.NaN setzen, der speziell dafür ausgelegt ist, Fehler in mathematischen Ausdrücken korrekt zu verbreiten. Selbst in diesem Fall dient der finally-Block als Dokumentation dafür, dass f.baz() an einer Art Bereinigung beteiligt ist.

10
Max Lybbert

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

3
Sascha

Ja, da wäre etwas sehr kritisch falsch.

Das heißt, Ihr Code würde nur ausführen, wenn ein Fehler vorliegt.

Anweisungen in finally werden immer ausgeführt, unabhängig davon, ob eine Ausnahme ausgelöst wird. Das ist der Punkt.

2
Noon Silk

endlich Block, der speziell zum Zeitpunkt der Ausnahmeverhinderung verwendet wird. Wenn ein Laufzeitfehler auftritt, kann das Programm zum Abbruch führen. Zu diesem Zeitpunkt wird also 'finally block' aufgerufen, bevor das Programm beendet wird. Normalerweise enthält 'finally' Anweisungen zum Schließen der Verbindung, Speicheroperationen und Dateieingaben sowie Ausgabeoperationen. 

2
janasainik

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

1
fastcodejava

Vor zwei Monaten schrieb ich einen Beitrag aus dem Grund für 'endlich' in einem try/catch.

In dem Bewusstsein, dass ein Link die Bearbeitung im Wiki-Stil hier behindert. 

Es ist ein in sich geschlossener Beitrag, der durch einfaches Kopieren nicht funktionieren würde, da Sie den Folgekommentar verpassen würden, der ebenfalls einen Mehrwert ergibt.

0
qnoid