Statische Codeanalysatoren wie Fortify "beschweren" sich, wenn eine Ausnahme in einen finally
Block geworfen wird Using a throw statement inside a finally block breaks the logical progression through the try-catch-finally
. Normalerweise stimme ich dem zu. Aber vor kurzem bin ich auf diesen Code gestoßen:
SomeFileWriter writer = null;
try {
//init the writer
//write into the file
} catch (...) {
//exception handling
} finally {
if (writer!= null) writer.close();
}
Wenn das writer
nicht richtig geschlossen werden kann, löst die writer.close()
Methode eine Ausnahme aus. Eine Ausnahme sollte geworfen werden, da die Datei (höchstwahrscheinlich) nach dem Schreiben nicht gespeichert wurde.
Ich könnte eine zusätzliche Variable deklarieren, sie setzen, wenn beim Schließen ein Fehler auftritt, writer
und nach dem finally-Block eine Ausnahme auslösen. Aber dieser Code funktioniert einwandfrei und ich bin nicht sicher, ob ich ihn ändern soll oder nicht.
Was sind die Nachteile des Auslösens einer Ausnahme innerhalb des finally
Blocks?
Antworten:
Grundsätzlich dienen
finally
Klauseln dazu, die ordnungsgemäße Freigabe einer Ressource sicherzustellen. Wenn jedoch eine Ausnahme innerhalb des finally-Blocks ausgelöst wird, erlischt diese Garantie. Schlimmer noch, wenn Ihr Hauptcodeblock eine Ausnahme auslöst, wird diese durch die im Block ausgelöste Ausnahmefinally
ausgeblendet. Es sieht so aus, als ob der Fehler durch den Aufruf von verursacht wurdeclose
, nicht aus dem wahren Grund.Einige Leute folgen einem unangenehmen Muster von verschachtelten Ausnahmebehandlungsroutinen und verschlucken alle Ausnahmen, die im
finally
Block geworfen werden .In älteren Java-Versionen können Sie diesen Code "vereinfachen", indem Sie Ressourcen in Klassen einschließen, die diese "sichere" Bereinigung für Sie durchführen. Ein guter Freund von mir erstellt eine Liste anonymer Typen, die jeweils die Logik für die Bereinigung ihrer Ressourcen enthalten. Dann durchläuft sein Code einfach die Liste und ruft die dispose-Methode innerhalb des
finally
Blocks auf.quelle
Was Travis Parks sagte, ist wahr, dass Ausnahmen im
finally
Block alle Rückgabewerte oder Ausnahmen von dentry...catch
Blöcken verbrauchen .Wenn Sie jedoch Java 7 verwenden, können Sie das Problem mithilfe eines Try-with-Resources- Blocks lösen . Laut Dokumentation wird
java.lang.AutoCloseable
der Try-with-Resources-Block für Sie geschlossen , solange Ihre Ressource implementiert ist (die meisten Bibliotheksschreiber / -leser tun dies jetzt). Der zusätzliche Vorteil hierbei ist, dass jede Ausnahme, die beim Schließen auftritt, unterdrückt wird, sodass der ursprüngliche Rückgabewert oder die ursprüngliche Ausnahme überschritten wird.Von
Zu
http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
quelle
close()
. "Jede Ausnahme, die beim Schließen auftritt, wird unterdrückt, wodurch der ursprüngliche Rückgabewert oder die ursprüngliche Ausnahme überschritten wird" - dies ist einfach nicht wahr . Eine Ausnahme von derclose()
Methode wird nur dann unterdrückt, wenn eine andere Ausnahme vom try / catch-Block ausgelöst wird. Wenn dertry
Block also keine Ausnahme auslöst, wird die Ausnahme von derclose()
Methode ausgelöst (und der Rückgabewert wird nicht zurückgegeben). Es kann sogar aus dem aktuellencatch
Block abgefangen werden .Ich denke, das müssen Sie von Fall zu Fall klären. In einigen Fällen ist das, was der Analysator sagt, insofern richtig, als der Code, den Sie haben, nicht so gut ist und ein Umdenken erfordert. Es kann aber auch andere Fälle geben, in denen das Werfen oder sogar das erneute Werfen das Beste ist. Es ist nicht etwas, das Sie beauftragen können.
quelle
Dies wird eher eine konzeptionelle Antwort darauf sein, warum diese Warnungen auch bei Ansätzen zum Ausprobieren von Ressourcen auftreten können. Es ist leider auch nicht die einfache Art von Lösung, die Sie sich erhoffen.
Fehlerbehebung kann nicht fehlschlagen
finally
modelliert einen Kontrollfluss nach einer Transaktion, der unabhängig davon ausgeführt wird, ob eine Transaktion erfolgreich ist oder fehlschlägt.Im Fehlerfall wird
finally
die Logik erfasst, die während der Wiederherstellung eines Fehlers ausgeführt wird, bevor dieser vollständig wiederhergestellt wurde (bevor wir unsercatch
Ziel erreichen).Stellen Sie sich das konzeptuelle Problem präsentiert es einen Fehler in der Begegnung Mitte von einem Fehler zurückzugewinnen.
Stellen Sie sich einen Datenbankserver vor, auf dem versucht wird, eine Transaktion festzuschreiben, die zur Hälfte fehlschlägt (der Server hat in der Mitte nicht genügend Arbeitsspeicher). Jetzt möchte der Server die Transaktion auf einen Punkt zurücksetzen, an dem nichts passiert ist. Stellen Sie sich jedoch vor, es stößt beim Zurücksetzen auf einen weiteren Fehler. Jetzt haben wir eine halb festgeschriebene Transaktion in der Datenbank - die Atomarität und die Unteilbarkeit der Transaktion sind jetzt aufgehoben, und die Integrität der Datenbank wird jetzt gefährdet.
Dieses konzeptionelle Problem tritt in jeder Sprache auf, die sich mit Fehlern befasst, unabhängig davon, ob es sich um C mit manueller Fehlercode-Weitergabe oder C ++ mit Ausnahmen und Destruktoren oder Java mit Ausnahmen und handelt
finally
.finally
kann nicht in Sprachen fehlschlagen, die es auf die gleiche Weise bereitstellen, wie Destruktoren in C ++ beim Auftreten von Ausnahmen nicht fehlschlagen können.Die einzige Möglichkeit, dieses konzeptionelle und schwierige Problem zu vermeiden, besteht darin, sicherzustellen, dass beim Zurücksetzen von Transaktionen und Freigeben von Ressourcen in der Mitte möglicherweise keine rekursive Ausnahme / kein rekursiver Fehler auftritt.
Das einzig sichere Design ist also ein Design, bei dem
writer.close()
es unmöglich ist, einen Fehler zu machen. Normalerweise gibt es im Entwurf Möglichkeiten, um Szenarien zu vermeiden, in denen solche Dinge mitten in der Wiederherstellung fehlschlagen können, was dies unmöglich macht.Dies ist leider der einzige Weg - die Fehlerbehebung kann nicht fehlschlagen. Der einfachste Weg, dies sicherzustellen, besteht darin, diese Arten von "Ressourcenfreigabe" - und "Nebenwirkungen" -Funktionen nicht mehr funktionsfähig zu machen. Es ist nicht einfach - eine ordnungsgemäße Fehlerbehebung ist schwierig und leider auch schwer zu testen. Um dies zu erreichen, müssen Sie jedoch sicherstellen, dass bei Funktionen, die "zerstören", "schließen", "herunterfahren", "zurücksetzen" usw., möglicherweise keine externen Fehler auftreten, da solche Funktionen häufig auftreten müssen aufgerufen werden, während ein bestehender Fehler behoben wird.
Beispiel: Protokollierung
Angenommen, Sie möchten Daten in einem
finally
Block protokollieren . Dies ist oft ein großes Problem, es sei denn, die Protokollierung kann nicht fehlschlagen . Die Protokollierung kann mit ziemlicher Sicherheit fehlschlagen, da möglicherweise mehr Daten an die Datei angehängt werden sollen, und dies kann leicht zu vielen Fehlern führen.Die Lösung hier ist, es so zu machen, dass jede in
finally
Blöcken verwendete Protokollierungsfunktion nicht zum Aufrufer geworfen werden kann (es könnte fehlschlagen, aber es wird nicht geworfen). Wie könnten wir das machen? Wenn Ihre Sprache das Werfen im Kontext von "Endlich" zulässt, vorausgesetzt, es gibt einen verschachtelten Try / Catch-Block, ist dies eine Möglichkeit, dem Aufrufer das Werfen zu vermeiden, indem Ausnahmen verschluckt und in Fehlercodes umgewandelt werden Prozess oder Thread, der separat und außerhalb eines vorhandenen Fehlerbehebungsstapels fehlschlagen kann, werden beendet. Solange Sie mit diesem Prozess kommunizieren können, ohne dass die Möglichkeit besteht, dass ein Fehler auftritt, ist dies auch ausnahmesicher, da das Sicherheitsproblem in diesem Szenario nur besteht, wenn wir rekursiv aus demselben Thread heraus werfen.In diesem Fall können wir mit Protokollierungsfehlern davonkommen, vorausgesetzt, dass sie nicht ausgelöst werden, da das Fehlschlagen der Protokollierung und das Nichtstun nicht das Ende der Welt ist (es gehen keine Ressourcen verloren oder es werden keine Nebeneffekte zurückgesetzt, z. B.).
Wie auch immer, ich bin sicher, Sie können sich bereits vorstellen, wie unglaublich schwierig es ist, eine Software wirklich ausnahmesicher zu machen. Möglicherweise ist es nicht erforderlich, dies in vollem Umfang zu prüfen, es sei denn, es handelt sich um die unternehmenskritischste Software. Beachten Sie jedoch, wie Sie wirklich Ausnahmesicherheit erreichen können, da selbst sehr universelle Bibliotheksautoren häufig hier fummeln und die gesamte Ausnahmesicherheit Ihrer Anwendung durch die Verwendung der Bibliothek zunichte machen.
SomeFileWriter
Wenn Sie
SomeFileWriter
die Option "inside" verwenden könnenclose
, ist dies im Allgemeinen nicht mit der Ausnahmebehandlung kompatibel, es sei denn, Sie versuchen niemals, sie in einem Kontext zu schließen, in dem eine vorhandene Ausnahme behoben wird. Wenn der Code dafür außerhalb Ihrer Kontrolle liegt, handelt es sich möglicherweise um SOL. Es lohnt sich jedoch, die Autoren über dieses offensichtliche Problem mit der Ausnahmesicherheit zu informieren. Wenn es in Ihrer Kontrolle liegt, ist es meine Hauptempfehlung, sicherzustellen, dass das Schließen mit allen erforderlichen Mitteln nicht fehlschlagen kann.Stellen Sie sich vor, ein Betriebssystem könnte eine Datei tatsächlich nicht schließen. Jetzt wird jedes Programm, das versucht, eine Datei beim Herunterfahren zu schließen, nicht mehr heruntergefahren . Was sollen wir jetzt tun? Halten Sie einfach die Anwendung offen und ignorieren Sie das Problem (möglicherweise in Ordnung, wenn es nicht so kritisch ist). Das sicherste Design: Machen Sie es so, dass es unmöglich ist, eine Datei nicht zu schließen.
quelle