forma correcta de cerrar corrientes anidados y escritores en Java [duplicado]

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

  •  22-08-2019
  •  | 
  •  

Pregunta

    

Esta pregunta ya tiene una respuesta aquí:

         

Nota: Esta pregunta y la mayoría de sus respuestas datan de antes de la liberación de Java 7. Java 7 proporciona funcionalidad de gestión de recursos automáticos para hacer esto facilmente. Si está utilizando Java 7 o posterior debe avanzar a la respuesta de Ross Johnson .


Lo que se considera la mejor manera, más completa para cerrar corrientes anidados en Java? Por ejemplo, considere la configuración:

FileOutputStream fos = new FileOutputStream(...)
BufferedOS bos = new BufferedOS(fos);
ObjectOutputStream oos = new ObjectOutputStream(bos);

Yo entiendo la operación de cierre tiene que estar asegurado (probablemente mediante el uso de una cláusula por fin). Lo que me pregunto acerca es decir, es necesario hacer explícita de que las corrientes anidados están cerrados, o es suficiente con sólo asegúrese de cerrar el flujo exterior (OOS)?

Una cosa que me di cuenta, al menos, tratar con este ejemplo concreto, es que las corrientes internas sólo parecen tirar FileNotFoundExceptions. Lo que parece dar a entender que no hay técnicamente una necesidad de preocuparse por cerrarlos si fallan.

Esto es lo que escribió un colega:


Técnicamente, si se implementa la derecha, cerrando la más externa corriente (OOS) debería ser suficiente. Sin embargo, la aplicación parece estar defectuoso.

Ejemplo: BufferedOutputStream hereda close () de FilterOutputStream, que lo define como:

 155       public void close() throws IOException {
 156           try {
 157             flush();
 158           } catch (IOException ignored) {
 159           }
 160           out.close();
 161       }

Sin embargo, si flush () lanza una excepción de tiempo de ejecución, por alguna razón, a continuación, out.close () nunca será llamado. Por lo que parece "más segura" (pero feo) a sobre todo preocuparse por el cierre de FOS, que está guardando el archivo abierto.


Lo que se considera que las manos hacia abajo mejor, cuando-te-absolutamente-necesidad-a-ser-Seguro, acercamiento a cerrar corrientes anidados?

Y ¿hay documentación oficial de Java / Sol que tienen que ver con esto en detalle?

¿Fue útil?

Solución

Yo suelo hacer lo siguiente. En primer lugar, definir una plantilla de método de clase basada tratar con el intento desastre / catch

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);
                }
            }
        }
    }
}

Tenga en cuenta la "pendiente" excepción -. Este se encarga del caso en que una excepción lanzada durante cerca ocultaría una excepción que realmente puede ser que se preocupan por

El fin intenta cerrar desde el exterior de cualquier corriente decorada en primer lugar, por lo que si usted tenía un BufferedWriter envolver una FileWriter, tratamos de cerrar la primera BuffereredWriter, y si eso no funciona, todavía tratan de cerrar la misma FileWriter. (Tenga en cuenta que la definición de que se pueda cerrar exige una estrecha () para ignorar la llamada si la corriente está ya cerrado)

Puede utilizar la clase anterior de la siguiente manera:

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
}

El uso de este enfoque que nunca tenga que preocuparse por el try / catch / finally para hacer frente a cierre de archivos de nuevo.

Si esto es demasiado pesado para su uso, al menos pensar en el siguiente try / catch y el "pendiente" enfoque de variables que utiliza.

Otros consejos

Al cerrar corrientes encadenadas, sólo tiene que cerrar la secuencia más externa. Cualquier error se propagarán en la cadena y ser capturados.

Consulte Java O Flujos de E / para más detalles.

Para abordar la cuestión

  

Sin embargo, si flush () lanza una excepción de tiempo de ejecución, por alguna razón, entonces out.close () nunca serán llamados.

Esto no es correcto. Después de coger y hacer caso omiso de esta excepción, la ejecución recoger una copia de seguridad después de que el bloque catch y se ejecutará la declaración out.close().

Su colega hace un buen punto acerca de la Tiempo de ejecución Excepción. Si es absolutamente necesario la corriente que ser cerrado, siempre se puede tratar de cerrar cada uno de ellos individualmente, de afuera hacia adentro, con parada en la primera excepción.

En la era de Java 7, Try-con-recursos es sin duda la forma ir. Como se mencionó en varias respuestas anteriores, la solicitud de cierre se propaga desde la corriente más externa de la corriente interna. Por lo que una sola estrecha es todo lo que se requiere.

try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(f))) {
  // do something with ois
}

Hay sin embargo un problema con este patrón. El try-con-recursos no es consciente de la FileInputStream interior, por lo que si el constructor ObjectInputStream lanza una excepción, el FileInputStream no se cierra nunca (hasta que el recolector de basura se pone a la misma). La solución es ...

try (FileInputStream fis = new FileInputStream(f); ObjectInputStream ois = new ObjectInputStream(fis)) {
  // do something with ois
}

Esto no es tan elegante, pero es más robusto. Si esto es en realidad un problema dependerá de qué excepciones puede ser lanzado durante la construcción del objeto (s) externa. ObjectInputStream puede lanzar IOException que bien puede quedar a cargo de una aplicación sin necesidad de terminar. Muchas clases de flujo sólo se lanzan excepciones sin marcar, que bien puede resultar en la terminación de la aplicación.

Es una buena práctica utilizar Apache Commons para manejar objetos relacionados IO.

En la cláusula finally uso IOUtils

IOUtils.closeQuietly (bWriter); IOUtils.closeQuietly (oWritter);

Fragmento de código a continuación.

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);
}

El colega plantea un punto interesante, y hay motivos para argumentar en ambos sentidos.

En lo personal, me gustaría pasar por alto el RuntimeException, debido a que una excepción no comprobada significa un error en el programa. Si el programa es incorrecto, solucionarlo. No se puede "manejar" un mal programa en tiempo de ejecución.

Esta es una pregunta sorprendentemente difícil. (Incluso suponiendo que el código acquire; try { use; } finally { release; } es correcta.)

Si la construcción del decorador de falla, entonces usted no será el cierre de la secuencia subyacente. Por lo tanto, sí es necesario para cerrar la secuencia subyacente de forma explícita, ya sea en el fin después de su uso o, más diifcult después de entregar con éxito sobre el recurso al decorador).

Si una excepción hace que la ejecución fallar, es lo que realmente desea eliminar?

Algunos decoradores en realidad tienen recursos propios. La actual implementación de Sun de ZipInputStream por ejemplo, tiene asignada la pila de memoria no Java.

Se ha afirmado que (IIRC) dos tercios de los recursos utiliza en la biblioteca de Java se implementan de manera claramente incorrecta.

Mientras BufferedOutputStream cierra incluso en un IOException de flush, BufferedWriter cierra correctamente.

Mi consejo: Cerrar recursos tan directamente como sea posible y no dejar que ellos contaminan otro código. Otoh, puede pasar mucho tiempo en este tema - si se tira OutOfMemoryError es bueno que se comporten bien, pero otros aspectos de su programa son probablemente una prioridad más alta y código de la biblioteca es probablemente roto en esta situación de todos modos. Pero siempre que escribiría:

final FileOutputStream rawOut = new FileOutputStream(file);
try {
    OutputStream out = new BufferedOutputStream(rawOut);
    ... write stuff out ...
    out.flush();
} finally {
    rawOut.close();
}

(Mira: No hay trampa)

Y tal vez utilizar el Ejecutar Alrededor idioma.

El Java SE 7 try-con-recursos doesn 't parecen ser mencionado. Elimina la necesidad de hacer explícita su fin por completo, y me gusta bastante la idea.

Desafortunadamente, para el desarrollo de Android este dulce solo pueda obtenerse mediante el uso de Android Studio (creo) y orientación Kitkat y por encima de .

También usted no tiene que cerrar todas las corrientes anidados

comprobar esto http://ckarthik17.blogspot.com/2011/02/closing-nested -streams.html

utilizo para cerrar los flujos de este tipo, sin anidación try-catch en fin bloques

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 de Sun incluyen RuntimeExceptions en su documentación, como lo demuestra de InputStream leer (byte [], int, int) método; documentado como tirar NullPointerException y IndexOutOfBoundsException.

href="http://java.sun.com/javase/6/docs/api/java/io/FilterOutputStream.html#flush()" de FilterOutputStream rel="nofollow ras noreferrer"> () solamente se documenta como tirar IOException, por lo que en realidad no arrojar ningún RuntimeExceptions. Cualquier que podría ser lanzada más probable sería envuelto en una IIOException .

Aún podría lanzar una Error , pero no hay mucho que se pueda hacer con ellas; Sun recomienda que no intenta atraparlos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top