Schließen der Java-Ressource

8

Ich schreibe eine App, die eine Verbindung zu einer Website herstellt und eine Zeile daraus liest. Ich mache es so:

%Vor%

Ist es gut? Ich meine, ich schließe den BufferedReader in der letzten Zeile, aber ich schließe den InputStreamReader nicht. Soll ich einen eigenständigen InputStreamReader aus dem connection.getInputStream und einen BufferedReader aus dem eigenständigen InputStreamReader erstellen, um alle zwei Reader zu schließen? Ich denke, es wird besser sein, die abschließenden Methoden wie folgt in den finally Block zu stellen:

%Vor%

Aber es ist hässlich, weil die Closing-Methoden eine Exception auslösen können, also muss ich sie behandeln oder werfen.

Welche Lösung ist besser? Oder was wäre die beste Lösung?

    
Bob 15.06.2010, 11:01
quelle

7 Antworten

6

Das allgemeine Idiom für Ressourcenerwerb und -freigabe in Java ist:

%Vor%

Hinweis:

  • try sollte dem Erwerb sofort folgen. Dies bedeutet, dass Sie es nicht in den Decorator einwickeln und die Sicherheit beibehalten können (und das Entfernen von Leerzeichen oder das Einfügen von Dingen in eine Zeile hilft nicht:).
  • Eine Veröffentlichung pro finally , andernfalls ist sie nicht ausnahmslos sicher.
  • Vermeiden Sie null , verwenden Sie final . Andernfalls haben Sie unordentlichen Code und Potenzial für NPEs.
  • Im Allgemeinen muss der Decorator nicht geschlossen werden, es sei denn, ihm ist eine weitere Ressource zugeordnet. In der Regel müssen Sie die Ausgaben jedoch leeren, vermeiden Sie dies jedoch im Ausnahmefall.
  • Die Ausnahme sollte entweder an den Aufrufer übergeben oder von einem umgebenden try -Block abgefangen werden (Java führt Sie hier in die Irre).

Sie können diesen Unsinn mit dem Execute Around idiom abstrahieren, also Sie Wiederhole dich nicht (schreibe einfach eine Menge Text).

    
Tom Hawtin - tackline 15.06.2010, 11:20
quelle
2

Es reicht, den BufferedReader zu schließen - dies schließt auch den zugrunde liegenden Reader.

Yishai hat ein schönes Muster gepostet , um die Streams zu schließen (Schließen könnte eine weitere Ausnahme auslösen).

    
Andreas_D 15.06.2010 11:07
quelle
2
  

Ist es gut? Ich meine, ich schließe den BufferedReader in der letzten Zeile, aber ich schließe den InputStreamReader nicht.

Abgesehen davon, dass es in der finally gemacht werden sollte (damit die Schließung auch im Falle einer Ausnahme gewährleistet ist), ist es in Ordnung. Die Java-IO-Klassen verwenden das Decorator-Muster. Der Abschluss wird an die zugrunde liegenden Streams delegiert.

  

Aber es ist hässlich, weil die Closing-Methoden eine Exception auslösen können, also muss ich sie handhaben oder werfen.

Wenn das Schließen eine Ausnahme auslöst, bedeutet dies oft, dass die andere Seite geschlossen oder gelöscht wurde, was völlig außerhalb Ihrer Kontrolle liegt. Sie können es höchstens protokollieren oder ignorieren. In einer einfachen Anwendung würde ich es einfach ignorieren. In einer missionskritischen Anwendung würde ich es loggen, nur um sicher zu sein.

In einer Nut kann Ihr Code wie folgt umgeschrieben werden:

%Vor%

In Java 7 gibt es eine automatische Ressourcenbehandlung, die Ihren Code so übersichtlich wie:

gemacht hat %Vor%

Siehe auch:

BalusC 15.06.2010 11:22
quelle
2
%Vor%

Sie deklarieren eine Variable, ohne sie zuzuweisen ( null zählt nicht - in diesem Fall ist es eine nutzlose Zuweisung). Dies ist ein Code "Geruch" in Java (ref Wirksames Java ; Code abgeschlossen für mehr auf Variablendeklaration).

%Vor%

Zuerst müssen Sie nur den obersten Stream-Decorator schließen ( br schließt isr ). Zweitens, wenn br.close() eine Ausnahme auslöst, würde isr.close() nicht aufgerufen, also ist dies kein Soundcode. Unter bestimmten Ausnahmebedingungen verdeckt Ihr Code die ursprüngliche Ausnahme mit NullPointerException .

%Vor%

Wenn das (zugegebenermaßen unwahrscheinliche) Ereignis, dass der InputStreamReader -Konstruktor irgendeine Art von Laufzeitausnahme ausgelöst hat, würde der Stream von der Verbindung nicht geschlossen werden.

Nutzen Sie die Schnittstelle Closeable , um die Redundanz zu reduzieren.

So würde ich Ihren Code schreiben:

%Vor%

Beachten Sie Folgendes:

  • Egal, welche Art von Ausnahme ausgelöst wird (Laufzeitumgebung oder überprüft) oder wo der Code keine Streamressourcen verliert
  • es gibt keinen catch-Block; Ausnahmen sollten bis dahin weitergegeben werden, wo der Code eine sinnvolle Entscheidung über die Fehlerbehandlung treffen kann; Wenn diese Methode der richtige Ort wäre, würden Sie alle oben genannten mit try / catch
  • umgeben

Vor einiger Zeit habe ich einige Zeit darüber nachgedacht, wie ich Vermeiden Sie das Lecken von Ressourcen / Daten, wenn etwas schief geht .

    
McDowell 15.06.2010 12:01
quelle
1
  

Ich denke, es wird besser sein, die   Schließmethoden im finally-Block

Ja, immer. Da eine Ausnahme auftreten kann und Ressourcen nicht ordnungsgemäß freigegeben / geschlossen werden.

Sie müssen nur den äußersten Leser schließen, da er dafür verantwortlich ist, alle umgebenden Leser zu schließen.

Ja, es ist hässlich ... vorerst. Ich denke, es gibt Pläne für eine automatische Ressourcenverwaltung in Java.

    
bruno conde 15.06.2010 11:08
quelle
1

Ich würde Apache Commons IO dafür verwenden, wie andere vorgeschlagen haben, hauptsächlich IOUtils.toString (InputStream) und IOUtils.closeQuietly(InputStream) :

%Vor%     
Sean Patrick Floyd 15.06.2010 12:32
quelle
0

Sie brauchen für java.io keine verschachtelten Anweisungen für verschachtelte Streams und Reader. Es ist sehr selten, dass Sie mehr als eine Sache in einer einzigen schließen müssen - die meisten Konstruktoren können eine Ausnahme auslösen, also würden Sie versuchen, Dinge zu schließen, die Sie noch nicht erstellt haben.

Wenn Sie den Stream schließen möchten, ob der Lesevorgang erfolgreich ist oder nicht, müssen Sie ihn in ein finally einfügen.

Weisen Sie Variablen nicht null zu und vergleichen Sie sie dann, um zu sehen, ob etwas früher passiert ist; Strukturieren Sie Ihr Programm stattdessen so, dass der Pfad, in dem Sie den Stream schließen, nur erreicht werden kann, wenn die Ausnahme nicht ausgelöst wird. Abgesehen von den Variablen, mit denen in For-Schleifen iteriert wird, sollten Variablen ihren Wert nicht ändern müssen - ich neige dazu, alles Endgültige zu markieren, es sei denn, es besteht die Notwendigkeit, etwas anderes zu tun. Wenn Sie Flags in Ihrem Programm haben, um Ihnen zu sagen, wie Sie zu dem gerade ausgeführten Code gekommen sind, und dann das Verhalten basierend auf diesen Flags zu ändern, ist das ein sehr prozeduraler (nicht einmal strukturierter) Programmierstil.

Wie Sie die try / catch / finally-Blöcke verschachteln, hängt davon ab, ob Sie die Ausnahmen, die von den verschiedenen Stufen ausgelöst werden, anders behandeln möchten.

%Vor%

getEncoding muss die Ergebnisse der getContentEncoding() und getContentType() der Verbindung prüfen, um die Kodierung der Webseite zu bestimmen; Ihr Code verwendet nur die Standardkodierung der Plattform, die möglicherweise falsch ist.

Ihr Beispiel ist jedoch in strukturierten Begriffen ungewöhnlich, da es sehr prozedural ist; normalerweise würden Sie das Drucken und das Abrufen in einem größeren System trennen und dem Clientcode erlauben, jede Ausnahme zu behandeln (oder manchmal eine benutzerdefinierte Ausnahme abzufangen und zu erstellen):

%Vor%

Wie McDowell sagte, müssen Sie möglicherweise den Eingabestream schließen, wenn new InputStreamReader wirft.

    
Pete Kirkham 15.06.2010 12:07
quelle