modo corretto di chiudere i flussi nidificati e scrittori in Java [duplicato]

StackOverflow https://stackoverflow.com/questions/884007

  •  22-08-2019
  •  | 
  •  

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?

È stato utile?

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 RuntimeExceptions nella loro documentazione, come dimostra di InputStream read (byte [], int, int) metodo; documentato come gettare NullPointerException e IndexOutOfBoundsException.

di FilterOutputStream rel="nofollow flush noreferrer"> () è documentato solo come lanciare IOException, quindi in realtà non gettare alcun RuntimeExceptions. 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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top