it-swarm.com.de

Ist ein finally-Block ohne catch-Block ein Java-Anti-Pattern?

Ich hatte nur eine ziemlich schmerzhafte Erfahrung bei der Fehlerbehebung bei einigen Codes, die so aussahen:

try {
   doSomeStuff()
   doMore()
} finally {
   doSomeOtherStuff()
}

Das Problem war schwer zu beheben, da doSomeStuff () eine Ausnahme auslöste, die wiederum dazu führte, dass doSomeOtherStuff () auch eine Ausnahme auslöste. Die zweite Ausnahme (ausgelöst durch den finally-Block) wurde bis zu meinem Code ausgelöst, hatte jedoch kein Handle für die erste Ausnahme (ausgelöst durch doSomeStuff ()), die die eigentliche Ursache des Problems war.

Wenn der Code dies stattdessen gesagt hätte, wäre das Problem ohne weiteres offensichtlich gewesen:

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

Meine Frage lautet also:

Wird ein finally-Block ohne catch-Block verwendet, ein bekanntes Java-Anti-Pattern? (Es scheint zweifellos eine nicht ohne weiteres erkennbare Unterklasse des offensichtlich bekannten Anti-Patterns "Verschlingen Sie keine Ausnahmen!" Zu sein.)

44
Jared

Im Allgemeinen ist dies kein Anti-Muster. Endlich blockiert man, um sicherzustellen, dass Sachen bereinigt werden, unabhängig davon, ob eine Ausnahme ausgelöst wird oder nicht. Der ganze Punkt der Ausnahmebehandlung ist, dass, wenn Sie nicht damit umgehen können, Sie es jemandem sprudeln lassen, der dies durch die relativ saubere Außerband-Signalisierungsausnahmebehandlung ermöglicht. Wenn Sie sicherstellen müssen, dass die Inhalte bereinigt werden, wenn eine Ausnahme ausgelöst wird, die Ausnahme im aktuellen Bereich jedoch nicht ordnungsgemäß verarbeitet werden kann, ist dies genau das Richtige. Vielleicht möchten Sie ein bisschen vorsichtiger sein, um sicherzustellen, dass Ihr Endblock nicht wirft.

62
dsimcha

Ich denke, das eigentliche "Anti-Pattern" hier ist etwas in einem finally-Block, der werfen kann, ohne einen Haken zu haben.

26
Logan Capaldo

Überhaupt nicht. 

Was falsch ist, ist der Code in der final. 

Denken Sie daran, dass letztendlich immer hingerichtet wird und es riskant ist (wie Sie gerade gesehen haben), etwas zu setzen, das eine Ausnahme auslöst.

16
OscarRyz

Es ist absolut nichts verkehrt, einen Versuch mit einem endgültigen und keinem Haken durchzuführen. Folgendes berücksichtigen:

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

Wenn eine Ausnahme ausgelöst wird und dieser Code nicht weiß, wie er damit umgehen soll, sollte die Ausnahme den Aufrufstapel zum Aufrufer aufblähen. In diesem Fall wollen wir den Stream noch bereinigen, daher ist es meiner Meinung nach absolut sinnvoll, einen try-Block ohne Haken zu haben.

10
Bryan Kyle

Ich denke, es ist weit davon entfernt, ein Anti-Pattern zu sein, und dies ist etwas, was ich sehr häufig mache, wenn es entscheidend ist, Ressourcen zuzuweisen, die während der Methodenausführung erhalten werden.

Eine Sache, die ich beim Umgang mit Datei-Handles (zum Schreiben) erledige, ist das Leeren des Streams vor dem Schließen mit der IOUtils.closeQuietly-Methode, die keine Ausnahmen auslöst:


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

Ich mache es aus folgenden Gründen so:

  • Es ist nicht absolut sicher, eine Ausnahme beim Schließen einer Datei zu ignorieren. Wenn es Bytes gibt, die noch nicht in die Datei geschrieben wurden, befindet sich die Datei möglicherweise nicht in dem Zustand, den der Aufrufer erwarten würde.
  • Wenn also während der flush () -Methode eine Ausnahme ausgelöst wird, wird sie an den Aufrufer weitergegeben, aber ich werde trotzdem sicherstellen, dass alle Dateien geschlossen sind. Die Methode IOUtils.closeQuietly (...) ist weniger ausführlich als der entsprechende try ... catch ... ignore me-Block;
  • Wenn Sie mehrere Ausgabeströme verwenden, ist die Reihenfolge für die Methode flush () wichtig. Die Streams, die durch Übergeben anderer Streams als Konstruktoren erstellt wurden, sollten zuerst geleert werden. Dasselbe gilt für die close () -Methode, aber das flush () ist meiner Meinung nach klarer.
5
Ravi Wallau

Meiner Meinung nach ist es eher so, dass finally mit einer catch auf ein Problem hinweist. Die Ressourcensprache ist sehr einfach:

acquire
try {
    use
} finally {
    release
}

In Java können Sie von fast überall eine Ausnahme haben. Häufig wirft der Erwerb eine geprüfte Ausnahme aus, die vernünftige Art, damit umzugehen, ist, einen Haken um die Anzahl zu setzen. Versuchen Sie nicht, eine häßliche Nullprüfung durchzuführen.

Wenn Sie wirklich anal sein wollten, sollten Sie beachten, dass es unter den Ausnahmen implizierte Prioritäten gibt. Zum Beispiel sollte ThreadDeath alles verstummen, egal ob es sich um Erwerb/Verwendung/Veröffentlichung handelt. Der korrekte Umgang mit diesen Prioritäten ist unansehnlich.

Abstrahieren Sie daher den Umgang mit Ihrer Ressource mit dem Idiom Execute Around.

Ich benutze try/finally in folgender Form:

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

Ich bevorzuge dies dem try/catch/schließlich (+ verschachteltes try/catch im final). Ich denke, dass es prägnanter ist und ich den catch nicht dupliziere (Ausnahme). 

1
Julien Grenier
try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

Tun Sie das auch nicht ... Sie haben einfach nur mehr Fehler versteckt (nicht genau versteckt ...), aber es wurde schwieriger, damit umzugehen. . 

Fangen Sie im Allgemeinen die Ausnahmen auf, die Sie einfangen müssen (geprüfte Ausnahmen), und behandeln Sie die anderen zum Testzeitpunkt. RuntimeExceptions sind für Programmierfehler gedacht - und Programmierfehler sind Dinge, die in einem ordnungsgemäß debuggierten Programm nicht auftreten sollten.

1
TofuBeer

try-finally kann Ihnen helfen, den Copy-Paste-Code zu reduzieren, falls eine Methode mehrere return-Anweisungen enthält. Betrachten Sie das folgende Beispiel (Android Java):

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) {
    Cursor cursor = db.rawQuery("SELECT * FROM table", null);
    if (cursor != null) { 
        try {
            if (cursor.getCount() == 0) { 
                return false;
            }
        } finally {
            // this will get executed even if return was executed above
            cursor.close();
        }
    }
    // database had rows, so do something...
    return true;
}

Wenn es keine finally-Klausel gab, müssen Sie möglicherweise cursor.close() zweimal schreiben: unmittelbar vor return false und auch nach der umgebenden if-Klausel.

0
Juuso Ohtonen

Ich würde sagen, ein Try-Block ohne Catch-Block ist ein Anti-Pattern. Zu sagen, "Endlich kein Fang ohne Fang", ist eine Untermenge von "Versuch keinen Fang ohne Fang".

0
billjamesdev

Try/Schließlich ist eine Möglichkeit, Ressourcen ordnungsgemäß freizugeben. Der Code im finally-Block sollte NIEMALS werfen, da er sich nur auf Ressourcen oder Status beziehen sollte, die vor dem Eintritt in den Try-Block erworben wurden.

Abgesehen davon denke ich, dass log4J fast ein Anti-Pattern ist. 

Wenn Sie ein laufendes Programm überprüfen möchten, verwenden Sie ein entsprechendes Inspektionswerkzeug (d. H. Einen Debugger, IDE oder im extremen Sinne einen Byte-Code-Weber, aber setzen Sie keine Protokollierungsstatements in alle wenigen Zeilen!).

In den beiden Beispielen, die Sie präsentieren, sieht das erste richtig aus. Die zweite enthält den Loggercode und führt einen Fehler ein. Im zweiten Beispiel unterdrücken Sie eine Ausnahme, wenn eine von den ersten beiden Anweisungen ausgelöst wird (dh Sie fangen sie ab und protokollieren sie, ohne sie erneut auszuführen. Dies ist etwas, was ich bei der Verwendung von log4j sehr häufig finde, und ist ein wirkliches Problem beim Anwendungsdesign. Grundsätzlich Mit Ihrer Änderung bewirken Sie, dass das Programm auf eine Art und Weise ausfällt, die für das System sehr schwer zu handhaben wäre, da Sie grundsätzlich weiter marschieren, als hätten Sie nie eine Ausnahme (sorta wie VB basic bei Fehler fortsetzen nächstes Konstrukt). . 

0
user188208