wirft Exception in finally-Blöcken


100

Gibt es eine elegante Möglichkeit, Ausnahmen zu behandeln, die in finallyBlock geworfen werden ?

Beispielsweise:

try {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
finally {
   try{
     resource.close();
   }
   catch( Exception ex ) {
     // Could not close the resource?
   }
}

Wie vermeidest du das try/ catchim finallyBlock?

Antworten:


72

Normalerweise mache ich das so:

try {
  // Use the resource.
} catch( Exception ex ) {
  // Problem with the resource.
} finally {
  // Put away the resource.
  closeQuietly( resource );
}

Anderswo:

protected void closeQuietly( Resource resource ) {
  try {
    if (resource != null) {
      resource.close();
    }
  } catch( Exception ex ) {
    log( "Exception during Resource.close()", ex );
  }
}

4
Ja, ich benutze eine sehr ähnliche Sprache. Aber dafür erstelle ich keine Funktion.
OscarRyz

9
Eine Funktion ist praktisch, wenn Sie die Redewendung an einigen Stellen in derselben Klasse verwenden müssen.
Darron

Die Prüfung auf Null ist redundant. Wenn die Ressource null war, sollte die aufrufende Methode fehlerhaft sein. Wenn die Ressource null ist, sollte dies wahrscheinlich protokolliert werden. Andernfalls wird eine mögliche Ausnahme stillschweigend ignoriert.
Dave Jarvis

14
Die Prüfung auf Null ist nicht immer redundant. Stellen Sie sich "resource = new FileInputStream (" file.txt ")" als erste Zeile des Versuchs vor. Bei dieser Frage ging es auch nicht um aspektorientierte Programmierung, die viele Menschen nicht verwenden. Das Konzept, dass die Ausnahme nicht einfach ignoriert werden sollte, wurde jedoch am kompaktesten durch Anzeigen einer Protokollanweisung behandelt.
Darron

1
Resource=> Closeable?
Dmitry Ginzburg

25

Normalerweise verwende ich eine der folgenden closeQuietlyMethoden org.apache.commons.io.IOUtils:

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}

3
Sie können diese Methode allgemeiner machen mit Closeable public static void closeQuietly (Closeable closeeable) {
Peter Lawrey

6
Ja, Closeable ist nett. Es ist eine Schande, dass viele Dinge (wie JDBC-Ressourcen) es nicht implementieren.
Darron

22

Wenn Sie Java 7 verwenden und resourceimplementieren AutoClosable, können Sie dies tun (am Beispiel von InputStream):

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}

8

Wahrscheinlich etwas übertrieben, aber vielleicht nützlich, wenn Sie Ausnahmen in die Luft sprudeln lassen und nichts aus Ihrer Methode heraus protokollieren können (z. B. weil es sich um eine Bibliothek handelt und Sie den aufrufenden Code lieber Ausnahmen und Protokollierung behandeln lassen möchten):

Resource resource = null;
boolean isSuccess = false;
try {
    resource = Resource.create();
    resource.use();
    // Following line will only run if nothing above threw an exception.
    isSuccess = true;
} finally {
    if (resource != null) {
        if (isSuccess) {
            // let close throw the exception so it isn't swallowed.
            resource.close();
        } else {
            try {
                resource.close();
            } catch (ResourceException ignore) {
                // Just swallow this one because you don't want it 
                // to replace the one that came first (thrown above).
            }
        }
    }
}

UPDATE: Ich habe mir das etwas genauer angesehen und einen großartigen Blog-Beitrag von jemandem gefunden, der klar mehr darüber nachgedacht hat als ich: http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make -mess-of-stream.html Er geht noch einen Schritt weiter und kombiniert die beiden Ausnahmen zu einer, was ich in einigen Fällen als nützlich erachten könnte.


1
+1 für den Blog-Link. Zusätzlich würde ich zumindest die ignoreAusnahme protokollieren
Denis Kniazhev

6

Ab Java 7 müssen Sie die Ressourcen in einem finally- Block nicht mehr explizit schließen , sondern können try verwenden Syntax with-resources verwenden. Die try-with-resources-Anweisung ist eine try-Anweisung, die eine oder mehrere Ressourcen deklariert. Eine Ressource ist ein Objekt, das geschlossen werden muss, nachdem das Programm damit beendet wurde. Die Anweisung try-with-resources stellt sicher, dass jede Ressource am Ende der Anweisung geschlossen wird. Jedes Objekt, das java.lang.AutoCloseable implementiert, einschließlich aller Objekte, die java.io.Closeable implementieren, kann als Ressource verwendet werden.

Nehmen Sie den folgenden Code an:

try( Connection con = null;
     Statement stmt = con.createStatement();
     Result rs= stmt.executeQuery(QUERY);)
{  
     count = rs.getInt(1);
}

Wenn eine Ausnahme auftritt, wird die close- Methode für jede dieser drei Ressourcen in umgekehrter Reihenfolge aufgerufen, in der sie erstellt wurden. Dies bedeutet, dass die Methode close zuerst für ResultSetm, dann für die Anweisung und am Ende für das Connection-Objekt aufgerufen wird.

Es ist auch wichtig zu wissen, dass alle Ausnahmen, die auftreten, wenn die close-Methoden automatisch aufgerufen werden, unterdrückt werden. Diese unterdrückten Ausnahmen können mit der in der Throwable- Klasse definierten Methode getsuppressed () abgerufen werden .

Quelle: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html


Es scheint unvollständig, dass diese Antwort den Unterschied im Verhalten zwischen diesem Ansatz und der Funktionsweise des im OP veröffentlichten Beispielcodes nicht erwähnt.
Nathan Hughes

2
Die Verwendung von try-with-resources löst beim Schließen eine Ausnahme aus, wenn der Teil im try-Block normal abgeschlossen wird, die close-Methode jedoch nicht, im Gegensatz zum OP-Code. Es scheint möglicherweise irreführend, es als Ersatz zu empfehlen, ohne die Verhaltensänderung anzuerkennen.
Nathan Hughes

Es wird keine Ausnahme ausgelöst, die Close-Methode wird automatisch aufgerufen und unterdrückt.
Soroosh

2
versuche den Fall, den ich beschrieben habe. try block wird normal abgeschlossen, close wirft etwas. Wenn Sie die Seite, auf die Sie den Link gepostet haben, erneut lesen, gilt die Unterdrückung nur, wenn der try-Block etwas auslöst.
Nathan Hughes

3

Das Ignorieren von Ausnahmen, die in einem 'finally'-Block auftreten, ist im Allgemeinen eine schlechte Idee, es sei denn, man weiß, wie diese Ausnahmen aussehen und welche Bedingungen sie darstellen. Im normalen try/finallyVerwendungsmuster versetzt der tryBlock die Dinge in einen Zustand, den der externe Code nicht erwartet, und denfinally Block stellt den Zustand dieser Dinge auf den vom externen Code erwarteten Zustand wieder her. Externer Code, der eine Ausnahme abfängt, erwartet im Allgemeinen, dass trotz der Ausnahme alles in a wiederhergestellt wurdenormalZustand. Angenommen, ein Code startet eine Transaktion und versucht dann, zwei Datensätze hinzuzufügen. Der Block "finally" führt eine Operation "Rollback, wenn nicht festgeschrieben" aus. Ein Aufrufer kann darauf vorbereitet sein, dass während der Ausführung der zweiten "Hinzufügen" -Operation eine Ausnahme auftritt, und kann erwarten, dass sich die Datenbank in dem Zustand befindet, in dem sie sich befand, bevor eine der beiden Operationen versucht wurde, wenn eine solche Ausnahme abgefangen wird. Wenn jedoch während des Rollbacks eine zweite Ausnahme auftritt, können schlimme Dinge passieren, wenn der Aufrufer Annahmen über den Datenbankstatus trifft. Der Rollback-Fehler stellt eine große Krise dar - eine, die nicht vom Code erfasst werden sollte, der lediglich eine Ausnahme "Fehler beim Hinzufügen eines Datensatzes" erwartet.

Meine persönliche Neigung wäre es, eine endgültige Methode zu haben, um auftretende Ausnahmen abzufangen und sie in eine "CleanupFailedException" zu packen, um zu erkennen, dass ein solcher Fehler ein großes Problem darstellt und eine solche Ausnahme nicht leichtfertig abgefangen werden sollte.


2

Eine Lösung, wenn die beiden Ausnahmen zwei verschiedene Klassen sind

try {
    ...
    }
catch(package1.Exception err)
   {
    ...
   }
catch(package2.Exception err)
   {
   ...
   }
finally
  {
  }

Aber manchmal kann man diesen zweiten Versuch nicht vermeiden. zB zum Schließen eines Streams

InputStream in=null;
try
 {
 in= new FileInputStream("File.txt");
 (..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
 }
catch(SQLException err)
 {
 //handle exception
 }
finally
 {
 //at the end, we close the file
 if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
 }

In Ihrem Fall sollte die Ressource bereinigt werden, wenn Sie eine "using" -Anweisung verwendet haben.
Chuck Conway

Mein schlechtes, ich gehe davon aus, dass es C # ist.
Chuck Conway

1

Warum möchten Sie den zusätzlichen Block vermeiden? Da der finally-Block "normale" Operationen enthält, die eine Ausnahme auslösen können UND Sie möchten, dass der finally-Block vollständig ausgeführt wird, MÜSSEN Sie Ausnahmen abfangen.

Wenn Sie nicht erwarten, dass der finally-Block eine Ausnahme auslöst, und Sie ohnehin nicht wissen, wie Sie mit der Ausnahme umgehen sollen (Sie würden nur die Stapelverfolgung ausgeben), lassen Sie die Ausnahme den Aufrufstapel aufblasen (entfernen Sie den try-catch aus dem finally-Block Block).

Wenn Sie die Eingabe reduzieren möchten, können Sie einen "globalen" äußeren Try-Catch-Block implementieren, der alle Ausnahmen abfängt, die in finally-Blöcken ausgelöst werden:

try {
    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }
} catch (Exception ex) {
    ...
}

2
-1 Auch für diesen. Was ist, wenn Sie versuchen, mehrere Ressourcen in einem einzigen Endblock zu schließen? Wenn das Schließen der ersten Ressource fehlschlägt, bleiben die anderen geöffnet, sobald die Ausnahme ausgelöst wird.
Outlaw Programmer

Aus diesem Grund habe ich Paul gesagt, dass Sie Ausnahmen abfangen MÜSSEN, wenn Sie sicherstellen möchten, dass der endgültige Block vollständig ist. Bitte lesen Sie die GANZE Antwort!
Eduard Wirch

1

Nach langem Überlegen finde ich den folgenden Code am besten:

MyResource resource = null;
try {
    resource = new MyResource();
    resource.doSomethingFancy();
    resource.close(); 
    resource = null;  
} finally {
    closeQuietly(resource)
}

void closeQuietly(MyResource a) {
    if (a!=null)
        try {
             a.close();
        } catch (Exception e) {
             //ignore
        }
}

Dieser Code garantiert Folgendes:

  1. Die Ressource wird freigegeben, wenn der Code fertig ist
  2. Ausnahmen, die beim Schließen der Ressource ausgelöst werden, werden nicht verarbeitet, ohne sie zu verarbeiten.
  3. Der Code versucht nicht, die Ressource zweimal zu schließen. Es wird keine unnötige Ausnahme erstellt.

Sie können auch vermeiden, resource.close () aufzurufen. resource = null im try-Block, dafür sind schließlich Blöcke gedacht. Beachten Sie auch, dass Sie keine Ausnahmen behandeln, die ausgelöst werden, während Sie "etwas Besonderes tun". Ich denke, ich bevorzuge es besser, infrastrukturelle Ausnahmen auf einer höheren Anwendungsebene im gesamten Stapel zu behandeln.
Paul

Die Datei resource.close () kann ebenfalls eine Ausnahme auslösen, dh wenn das Löschen des Puffers fehlschlägt. Diese Ausnahme sollte niemals verbraucht werden. Wenn der Stream jedoch aufgrund einer zuvor ausgelösten Ausnahme geschlossen wird, sollte die Ressource stillschweigend geschlossen werden, wobei die Ausnahme ignoriert und die Grundursache beibehalten wird.
Grogi

0

Wenn Sie können, sollten Sie zunächst testen, um den Fehlerzustand zu vermeiden.

try{...}
catch(NullArgumentException nae){...}
finally
{
  //or if resource had some useful function that tells you its open use that
  if (resource != null) 
  {
      resource.Close();
      resource = null;//just to be explicit about it was closed
  }
}

Außerdem sollten Sie wahrscheinlich nur Ausnahmen abfangen, von denen Sie sich erholen können. Wenn Sie sich nicht erholen können, lassen Sie es auf die oberste Ebene Ihres Programms übertragen. Wenn Sie nicht auf eine Fehlerbedingung testen können, müssen Sie Ihren Code mit einem Try-Catch-Block umgeben, wie Sie es bereits getan haben (obwohl ich empfehlen würde, immer noch bestimmte, erwartete Fehler abzufangen).


Das Testen von Fehlerbedingungen ist im Allgemeinen eine gute Praxis, einfach weil Ausnahmen teuer sind.
Dirk Vollmar

"Defensive Programming" ist ein veraltetes Paradigma. Der aufgeblähte Code, der sich aus dem Testen aller Fehlerzustände ergibt, verursacht schließlich mehr Probleme als er löst. TDD und Umgang mit Ausnahmen ist der moderne Ansatz IMHO
Joe Soul-Bringer

@Joe - Ich bin nicht anderer Meinung, wenn es darum geht, alle Fehlerbedingungen zu testen, aber manchmal ist es sinnvoll, insbesondere angesichts der (normalerweise) unterschiedlichen Kosten einer einfachen Überprüfung, die Ausnahme gegenüber der Ausnahme selbst zu vermeiden.
Ken Henderson

1
-1 Hier kann resource.Close () eine Ausnahme auslösen. Wenn Sie zusätzliche Ressourcen schließen müssen, führt die Ausnahme dazu, dass die Funktion zurückgegeben wird und diese geöffnet bleiben. Das ist der Zweck des zweiten Versuchs / Fangs im OP.
Outlaw Programmer

@Outlaw - Sie verpassen meinen Standpunkt, wenn Close eine Ausnahme auslöst und die Ressource geöffnet ist, indem Sie die Ausnahme erfassen und unterdrücken. Wie kann ich das Problem beheben? Deshalb lasse ich es sich verbreiten (es ist ziemlich selten, dass ich mich erholen kann, wenn es noch offen ist).
Ken Henderson

0

Sie könnten dies in eine andere Methode umgestalten ...

public void RealDoSuff()
{
   try
   { DoStuff(); }
   catch
   { // resource.close failed or something really weird is going on 
     // like an OutOfMemoryException 
   }
}

private void DoStuff() 
{
  try 
  {}
  catch
  {
  }
  finally 
  {
    if (resource != null) 
    {
      resource.close(); 
    }
  }
}

0

Normalerweise mache ich das:

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}

Begründung: Wenn ich mit der Ressource fertig bin und das einzige Problem darin besteht, sie zu schließen, kann ich nicht viel dagegen tun. Es macht auch keinen Sinn, den gesamten Thread zu beenden, wenn ich mit der Ressource sowieso fertig bin.

Dies ist einer der Fälle, in denen es zumindest für mich sicher ist, diese aktivierte Ausnahme zu ignorieren.

Bis heute hatte ich keine Probleme mit dieser Redewendung.


Ich würde es protokollieren, nur für den Fall, dass Sie in Zukunft einige Lecks finden. Auf diese Weise würden Sie wissen, woher sie (nicht) kommen könnten
Egwor

@ Egwor. Ich stimme mit Ihnen ein. Dies war nur ein kurzer Ausschnitt. Ich logge es auch ein und benutze wahrscheinlich einen Fang, etwas könnte mit der Ausnahme getan werden :)
OscarRyz

0
try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

Job erledigt. Keine Nulltests. Einzelfang, einschließlich Erwerbs- und Freigabeausnahmen. Natürlich können Sie das Execute Around-Idiom verwenden und müssen es für jeden Ressourcentyp nur einmal schreiben.


5
Was ist, wenn use (resource) Ausnahme A auslöst und resource.release () dann Ausnahme B auslöst? Ausnahme A ist verloren ...
Darron

0

Wechsel Resourcevon der besten Antwort zuCloseable

Streams implementiert CloseableSomit können Sie die Methode für alle Streams wiederverwenden

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}

0

Ich bin auf eine ähnliche Situation gestoßen, in der ich try nicht mit Ressourcen verwenden konnte, aber ich wollte auch die Ausnahme behandeln, die vom Schließen herrührt, und nicht nur protokollieren und ignorieren, wie es der closeQuietly-Mechanismus tut. In meinem Fall habe ich es eigentlich nicht mit einem Ausgabestream zu tun, daher ist der Fehler beim Schließen von größerem Interesse als ein einfacher Stream.

IOException ioException = null;
try {
  outputStream.write("Something");
  outputStream.flush();
} catch (IOException e) {
  throw new ExportException("Unable to write to response stream", e);
}
finally {
  try {
    outputStream.close();
  } catch (IOException e) {
    ioException = e;
  }
}
if (ioException != null) {
  throw new ExportException("Unable to close outputstream", ioException);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.