modo corretto di chiudere i flussi nidificati e scrittori in Java [duplicato]
Domanda
Questa domanda ha già una risposta qui:
Nota: Questa domanda e la maggior parte delle sue risposte risalgono a prima del rilascio di Java 7. Java 7 fornisce funzionalità di gestione automatica delle risorse per fare questo easilly. Se si utilizza Java 7 o poi si dovrebbe passare alla la risposta di Ross Johnson .
Quello che è considerato il miglior modo più completo per chiudere i flussi annidati in Java? Si consideri ad esempio la messa a punto:
FileOutputStream fos = new FileOutputStream(...)
BufferedOS bos = new BufferedOS(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);
Capisco l'operazione di chiusura deve essere assicurato (probabilmente utilizzando una clausola finally). Quello che mi chiedo è, è necessario fare in modo esplicito che i flussi di nidificate sono chiusi, o è abbastanza per fare solo assicurarsi di chiudere il flusso esterno (OOS)?
Una cosa che ho notato, almeno si occupano di questo esempio specifico, è che i flussi interni sembrano solo buttare FileNotFoundExceptions. Che sembrerebbe implicare che non ci sia tecnicamente un bisogno di preoccuparsi di chiusura di loro se non riescono.
Ecco quello che un collega ha scritto:
Tecnicamente, se fosse attuata a destra, chiudendo il più esterno flusso (OOS) dovrebbe essere sufficiente. Ma l'attuazione sembra viziata.
Esempio: BufferedOutputStream eredita close () da FilterOutputStream, che definisce come:
155 public void close() throws IOException {
156 try {
157 flush();
158 } catch (IOException ignored) {
159 }
160 out.close();
161 }
Tuttavia, se flush () genera un'eccezione di runtime per qualche ragione, allora out.close () non sarà mai chiamato. Così sembra "più sicuro" (ma brutto) per per lo più preoccuparsi di chiusura FOS, che sta mantenendo il file aperto.
quello che è considerato essere il mani-down meglio, quando-you-assolutamente-bisogno-di-essere-sicuri, volto a colmare flussi nidificate?
E ci sono Java / documentazione ufficiale di Sun che si occupano di questo particolare fine?
Soluzione
Di solito faccio quanto segue. In primo luogo, definire un metodo modello di classe in base a che fare con il try / catch pasticcio
import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public abstract class AutoFileCloser {
// the core action code that the implementer wants to run
protected abstract void doWork() throws Throwable;
// track a list of closeable thingies to close when finished
private List<Closeable> closeables_ = new LinkedList<Closeable>();
// give the implementer a way to track things to close
// assumes this is called in order for nested closeables,
// inner-most to outer-most
protected final <T extends Closeable> T autoClose(T closeable) {
closeables_.add(0, closeable);
return closeable;
}
public AutoFileCloser() {
// a variable to track a "meaningful" exception, in case
// a close() throws an exception
Throwable pending = null;
try {
doWork(); // do the real work
} catch (Throwable throwable) {
pending = throwable;
} finally {
// close the watched streams
for (Closeable closeable : closeables_) {
if (closeable != null) {
try {
closeable.close();
} catch (Throwable throwable) {
if (pending == null) {
pending = throwable;
}
}
}
}
// if we had a pending exception, rethrow it
// this is necessary b/c the close can throw an
// exception, which would remove the pending
// status of any exception thrown in the try block
if (pending != null) {
if (pending instanceof RuntimeException) {
throw (RuntimeException) pending;
} else {
throw new RuntimeException(pending);
}
}
}
}
}
Si noti l'eccezione "in sospeso" -. Questo si prende cura del caso in cui un eccezione generata durante vicino sarebbe mascherare un'eccezione potremmo veramente a cuore
Il fine cerca di chiudere dall'esterno di qualsiasi flusso decorato prima, quindi se si ha un BufferedWriter avvolgendo un FileWriter, cerchiamo di chiudere il BuffereredWriter prima, e se non funziona, comunque cercare di chiudere il FileWriter stesso. (Si noti che la definizione di Closeable richiede close () per ignorare la chiamata se il flusso è già chiusa)
È possibile utilizzare la classe di cui sopra nel seguente modo:
try {
// ...
new AutoFileCloser() {
@Override protected void doWork() throws Throwable {
// declare variables for the readers and "watch" them
FileReader fileReader =
autoClose(fileReader = new FileReader("somefile"));
BufferedReader bufferedReader =
autoClose(bufferedReader = new BufferedReader(fileReader));
// ... do something with bufferedReader
// if you need more than one reader or writer
FileWriter fileWriter =
autoClose(fileWriter = new FileWriter("someOtherFile"));
BufferedWriter bufferedWriter =
autoClose(bufferedWriter = new BufferedWriter(fileWriter));
// ... do something with bufferedWriter
}
};
// .. other logic, maybe more AutoFileClosers
} catch (RuntimeException e) {
// report or log the exception
}
Con questo approccio non dovrai mai preoccuparti per il try / catch / finally per affrontare la chiusura di nuovo i file.
Se questo è troppo pesante per il vostro uso, almeno pensato di seguire il try / catch e il "sospeso" approccio variabile che utilizza.
Altri suggerimenti
Quando si chiude flussi incatenati, avete solo bisogno di chiudere il flusso più esterno. Eventuali errori saranno propagate lungo la catena ed essere catturati.
Fare riferimento a Java O Flussi per i dettagli di I /.
Per affrontare il problema
Tuttavia, se flush () genera un'eccezione di runtime per qualche ragione, allora out.close () non verrà mai chiamato.
Questo non è giusto. Dopo si cattura e ignorare tale eccezione, l'esecuzione prenderà di nuovo dopo il blocco catch e verrà eseguita l'istruzione out.close()
.
Il vostro collega rende un buon punto sulla Runtime Exception. Se è assolutamente necessario il flusso per essere chiuso, si può sempre provare a chiudere ciascuno individualmente, dall'esterno verso l'interno, fermandosi alla prima eccezione.
In epoca Java 7, Try-con-le risorse è certamente il modo andare. Come menzionato in diverse risposte precedenti, la richiesta di chiusura propaga dal flusso esterno al flusso più interno. Quindi un unico vicino è tutto ciò che è richiesto.
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
// do something with ois
}
C'è però un problema con questo modello. La prova-con-le risorse non è a conoscenza del FileInputStream interna, quindi se il costruttore ObjectInputStream genera un'eccezione, il FileInputStream non è mai chiusa (fino a quando il garbage collector viene ad esso). La soluzione è ...
try (FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis)) {
// do something with ois
}
Questo non è elegante, ma è più robusto. Se questo è in realtà un problema dipenderà da quello che le eccezioni possono essere gettati durante la costruzione dell'oggetto esterno (s). ObjectInputStream può lanciare IOException che potrebbe ottenere gestito da un'applicazione senza terminare. Molte classi stream gettano solo eccezioni unchecked, che potrebbe comportare l'interruzione dell'applicazione.
Si tratta di una buona pratica di utilizzare Apache Commons per gestire gli oggetti correlati IO.
Nella clausola finally
uso IOUtils
IOUtils.closeQuietly (bWriter); IOUtils.closeQuietly (oWritter);
Snippet di codice di seguito.
BufferedWriter bWriter = null;
OutputStreamWriter oWritter = null;
try {
oWritter = new OutputStreamWriter( httpConnection.getOutputStream(), "utf-8" );
bWriter = new BufferedWriter( oWritter );
bWriter.write( xml );
}
finally {
IOUtils.closeQuietly(bWriter);
IOUtils.closeQuietly(oWritter);
}
Il collega solleva un punto interessante, e ci sono ragioni per ritenere in entrambi i modi.
Personalmente, vorrei ignorare la RuntimeException
, perché un'eccezione incontrollato significa un bug nel programma. Se il programma non è corretto, risolvere il problema. Non si può "gestire" un programma di male in fase di esecuzione.
Questa è una domanda sorprendentemente imbarazzante. (Anche supponendo il codice acquire; try { use; } finally { release; }
sia corretto.)
Se la costruzione della decoratrice non riesce, allora è solito essere la chiusura del flusso sottostante. Pertanto si ha bisogno di chiudere il flusso sottostante in modo esplicito, sia nel finalmente dopo l'uso o, più diifcult dopo aver consegnato con successo sulla risorsa per il decoratore).
Se un'eccezione provoca l'esecuzione di fallire, non si vuole veramente per irrigare?
Alcuni decoratori in realtà hanno risorse stesse. L'implementazione corrente di Sun ZipInputStream
per esempio ha non Java memoria heap allocata.
E 'stato affermato che (IIRC) due terzi delle risorse utilizza nella libreria Java sono implementati in modo chiaramente errata.
Mentre BufferedOutputStream
chiude anche su un IOException
da flush
, BufferedWriter
chiude correttamente.
Il mio consiglio: chiudere le risorse nel modo più diretto possibile e non lasciare che macchiano altro codice. OTOH, è possibile trascorrere troppo tempo su questo tema - se OutOfMemoryError
è gettato è bello comportarsi bene, ma altri aspetti del vostro programma sono probabilmente una priorità maggiore e codice della libreria è probabilmente rotto in questa situazione in ogni caso. Ma ho sempre scritto:
final FileOutputStream rawOut = new FileOutputStream(file);
try {
OutputStream out = new BufferedOutputStream(rawOut);
... write stuff out ...
out.flush();
} finally {
rawOut.close();
}
(Look: No cattura)
E forse utilizzare l'esecuzione Intorno idioma.
Il Java SE 7 prova-con-le risorse doesn 't sembra di essere menzionati. Si elimina la necessità di fare in modo esplicito una stretta del tutto, e mi piace molto l'idea.
Purtroppo, per lo sviluppo di Android questo dolce diventa disponibile solo utilizzando Android Studio (credo) e mira Kitkat e soprattutto .
Inoltre non dovete chiudere tutti i flussi di nidificate
controllare questo http://ckarthik17.blogspot.com/2011/02/closing-nested -streams.html
Io uso di chiudere i flussi di questo tipo, senza nidificazione try-catch nel infine blocchi
public class StreamTest {
public static void main(String[] args) {
FileOutputStream fos = null;
BufferedOutputStream bos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(new File("..."));
bos = new BufferedOutputStream(fos);
oos = new ObjectOutputStream(bos);
}
catch (Exception e) {
}
finally {
Stream.close(oos,bos,fos);
}
}
}
class Stream {
public static void close(AutoCloseable... array) {
for (AutoCloseable c : array) {
try {c.close();}
catch (IOException e) {}
catch (Exception e) {}
}
}
}
JavaDocs Sun includono RuntimeException
s nella loro documentazione, come dimostra di InputStream read (byte [], int, int) metodo; documentato come gettare NullPointerException e IndexOutOfBoundsException.
RuntimeException
s. Qualsiasi che potrebbe essere gettato sarebbe molto probabilmente avvolto in un IIOException
.
Si potrebbe ancora lanciare un Error
, ma non c'è molto che si può fare di quelli; Sun consiglia di non si tenta di catturarli.