outputstreamwriter ejemplo create java file-io outputstream writer

java - ejemplo - ¿Es necesario cerrar cada OutputStream y Writer anidados por separado?



outputstreamwriter android (7)

Estoy escribiendo un código:

OutputStream outputStream = new FileOutputStream(createdFile); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(gzipOutputStream));

¿Necesito cerrar cada transmisión o escritor como el siguiente?

gzipOutputStream.close(); bw.close(); outputStream.close();

¿O solo estará bien cerrar la última transmisión?

bw.close();


Asumiendo que todas las transmisiones se crean bien, sí, solo cerrar bw está bien con esas implementaciones de transmisiones ; Pero esa es una gran suposición.

Usaría try-with-resources ( tutorial ) para que cualquier problema que construya las secuencias posteriores que arroje excepciones no deje las secuencias anteriores suspendidas, por lo que no tiene que confiar en que la implementación de la secuencia tenga la llamada para cerrar la corriente subyacente:

try ( OutputStream outputStream = new FileOutputStream(createdFile); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream); BufferedWriter bw = new BufferedWriter(osw) ) { // ... }

Tenga en cuenta que ya no se llama close en absoluto.

Nota importante : para que los recursos de prueba con los cierren, debe asignar las secuencias a las variables a medida que las abre, no puede usar el anidamiento. Si usa la anidación, una excepción durante la construcción de una de las secuencias posteriores (por ejemplo, GZIPOutputStream ) dejará GZIPOutputStream cualquier secuencia construida por las llamadas anidadas dentro de ella. De JLS §14.20.3 :

Una instrucción try-with-resources se parametriza con variables (conocidas como recursos) que se inicializan antes de la ejecución del bloque try y se cierran automáticamente, en el orden inverso desde el que se inicializaron, después de la ejecución del bloque try .

Tenga en cuenta la palabra "variables" (mi énfasis) .

Por ejemplo, no hagas esto:

// DON''T DO THIS try (BufferedWriter bw = new BufferedWriter( new OutputStreamWriter( new GZIPOutputStream( new FileOutputStream(createdFile))))) { // ... }

... porque una excepción del constructor GZIPOutputStream(OutputStream) (que dice que puede lanzar IOException y escribe un encabezado en la secuencia subyacente) dejaría abierto FileOutputStream . Dado que algunos recursos tienen constructores que pueden arrojar y otros no, es una buena costumbre enumerarlos por separado.

Podemos verificar nuestra interpretación de esa sección JLS con este programa:

public class Example { private static class InnerMost implements AutoCloseable { public InnerMost() throws Exception { System.out.println("Constructing " + this.getClass().getName()); } @Override public void close() throws Exception { System.out.println(this.getClass().getName() + " closed"); } } private static class Middle implements AutoCloseable { private AutoCloseable c; public Middle(AutoCloseable c) { System.out.println("Constructing " + this.getClass().getName()); this.c = c; } @Override public void close() throws Exception { System.out.println(this.getClass().getName() + " closed"); c.close(); } } private static class OuterMost implements AutoCloseable { private AutoCloseable c; public OuterMost(AutoCloseable c) throws Exception { System.out.println("Constructing " + this.getClass().getName()); throw new Exception(this.getClass().getName() + " failed"); } @Override public void close() throws Exception { System.out.println(this.getClass().getName() + " closed"); c.close(); } } public static final void main(String[] args) { // DON''T DO THIS try (OuterMost om = new OuterMost( new Middle( new InnerMost() ) ) ) { System.out.println("In try block"); } catch (Exception e) { System.out.println("In catch block"); } finally { System.out.println("In finally block"); } System.out.println("At end of main"); } }

... que tiene la salida:

Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost In catch block In finally block At end of main

Tenga en cuenta que no hay llamadas para close allí.

Si arreglamos main :

public static final void main(String[] args) { try ( InnerMost im = new InnerMost(); Middle m = new Middle(im); OuterMost om = new OuterMost(m) ) { System.out.println("In try block"); } catch (Exception e) { System.out.println("In catch block"); } finally { System.out.println("In finally block"); } System.out.println("At end of main"); }

entonces recibimos las llamadas de close apropiadas:

Constructing Example$InnerMost Constructing Example$Middle Constructing Example$OuterMost Example$Middle closed Example$InnerMost closed Example$InnerMost closed In catch block In finally block At end of main

(Sí, dos llamadas a InnerMost#close son correctas; una es de Middle y la otra de try-with-resources).


En Java 7, hay una función de tutorial . No necesita cerrar explícitamente sus transmisiones, se encargará de eso.


Estará bien si solo cierra la última secuencia; la llamada de cierre también se enviará a las secuencias subyacentes.


No, el Stream o reader nivel superior se asegurará de que todos los flujos / lectores subyacentes estén cerrados.

Verifique la implementación del método close() de su flujo de nivel superior.


Prefiero usar la sintaxis try(...) (Java 7), p. Ej.

try (OutputStream outputStream = new FileOutputStream(createdFile)) { ... }


Puede cerrar la secuencia más externa, de hecho, no necesita retener todas las secuencias envueltas y puede usar Java 7 try-with-resources.

try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter( new GZIPOutputStream(new FileOutputStream(createdFile)))) { // write to the buffered writer }

Si se suscribe a YAGNI, o no lo va a necesitar, solo debe agregar el código que realmente necesita. No debería agregar código que imagina que podría necesitar, pero en realidad no hace nada útil.

Tome este ejemplo e imagine qué podría salir mal si no hiciera esto y cuál sería el impacto.

try ( OutputStream outputStream = new FileOutputStream(createdFile); GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream); OutputStreamWriter osw = new OutputStreamWriter(gzipOutputStream); BufferedWriter bw = new BufferedWriter(osw) ) { // ... }

Comencemos con FileOutputStream que llama a open para hacer todo el trabajo real.

/** * Opens a file, with the specified name, for overwriting or appending. * @param name name of file to be opened * @param append whether the file is to be opened in append mode */ private native void open(String name, boolean append) throws FileNotFoundException;

Si no se encuentra el archivo, no hay ningún recurso subyacente para cerrar, por lo que cerrarlo no hará ninguna diferencia. Si el archivo existe, debería estar lanzando una FileNotFoundException. Por lo tanto, no hay nada que ganar intentando cerrar el recurso solo desde esta línea.

La razón por la que necesita cerrar el archivo es cuando el archivo se abre correctamente, pero luego aparece un error.

GZIPOutputStream la próxima transmisión GZIPOutputStream

Hay un código que puede lanzar una excepción

private void writeHeader() throws IOException { out.write(new byte[] { (byte) GZIP_MAGIC, // Magic number (short) (byte)(GZIP_MAGIC >> 8), // Magic number (short) Deflater.DEFLATED, // Compression method (CM) 0, // Flags (FLG) 0, // Modification time MTIME (int) 0, // Modification time MTIME (int) 0, // Modification time MTIME (int) 0, // Modification time MTIME (int) 0, // Extra flags (XFLG) 0 // Operating system (OS) }); }

Esto escribe el encabezado del archivo. Ahora sería muy inusual para usted poder abrir un archivo para escribir pero no poder escribir ni siquiera 8 bytes, pero imaginemos que esto podría suceder y no cerramos el archivo después. ¿Qué le sucede a un archivo si no está cerrado?

No se obtienen escrituras sin vaciar, se descartan y, en este caso, no hay bytes escritos correctamente en la secuencia que, de todos modos, no están almacenados en este momento. Pero un archivo que no está cerrado no vive para siempre, en cambio FileOutputStream tiene

protected void finalize() throws IOException { if (fd != null) { if (fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else { /* if fd is shared, the references in FileDescriptor * will ensure that finalizer is only called when * safe to do so. All references using the fd have * become unreachable. We can call close() */ close(); } } }

Si no cierra un archivo, se cierra de todos modos, solo que no de inmediato (y como dije, los datos que quedan en un búfer se perderán de esta manera, pero no hay ninguno en este momento)

¿Cuál es la consecuencia de no cerrar el archivo de inmediato? En condiciones normales, posiblemente pierda algunos datos y posiblemente se quede sin descriptores de archivo. Pero si tiene un sistema donde puede crear archivos pero no puede escribirles nada, tiene un problema mayor. es decir, es difícil imaginar por qué está intentando crear este archivo repetidamente a pesar de que está fallando.

Tanto OutputStreamWriter como BufferedWriter no lanzan IOException en sus constructores, por lo que no está claro qué problema causarían. En el caso de BufferedWriter, puede obtener un OutOfMemoryError. En este caso, activará inmediatamente un GC, que como hemos visto cerrará el archivo de todos modos.


Si se han instanciado todos los flujos, entonces cerrar solo el más externo está bien.

La documentación sobre la interfaz que se puede cerrar establece ese método de cierre:

Cierra esta secuencia y libera todos los recursos del sistema asociados a ella.

Los recursos del sistema de liberación incluyen el flujo de cierre.

También establece que:

Si la secuencia ya está cerrada, invocar este método no tiene ningún efecto.

Entonces, si los cierra explícitamente después, no pasará nada malo.