Pregunta

Una de las cosas que siempre me molesta sobre el uso de lectores y secuencias en Java es que el método close () puede generar una excepción. Dado que es una buena idea colocar el método de cierre en un bloque finalmente, eso requiere una situación un poco incómoda. Usualmente uso esta construcción:

FileReader fr = new FileReader("SomeFile.txt");
try {
    try {
        fr.read();
    } finally {
        fr.close();
    }
} catch(Exception e) {
    // Do exception handling
}

Pero también he visto esta construcción:

FileReader fr = new FileReader("SomeFile.txt");
try {
    fr.read() 
} catch (Exception e) {
    // Do exception handling
} finally {
    try {
        fr.close();
    } catch (Exception e) {
        // Do exception handling
    }
}

Prefiero la primera construcción porque solo hay un bloque de captura y parece más elegante. ¿Hay alguna razón para preferir realmente la segunda o una construcción alternativa?

ACTUALIZACIÓN: ¿Habría una diferencia si señalara que tanto read como close solo arrojan IOExceptions? Por lo tanto, me parece probable que, si la lectura falla, el cierre fallará por la misma razón.

¿Fue útil?

Solución

Siempre iría por el primer ejemplo.

Si cerrar fuera a lanzar una excepción (en la práctica, eso nunca sucederá para un FileReader), ¿no sería la forma estándar de manejo que sea lanzar una excepción apropiada para la persona que llama? La estrecha excepción casi con certeza supera cualquier problema que haya tenido al usar el recurso. El segundo método es probablemente más apropiado si su idea de manejo de excepciones es llamar a System.err.println.

Hay un problema de hasta qué punto se deben lanzar las excepciones. ThreadDeath siempre debe volverse a lanzar, pero cualquier excepción dentro finalmente lo detendría. Del mismo modo, el error debe arrojarse más allá de RuntimeException y RuntimeException más allá de las excepciones marcadas. Si realmente quisieras, podrías escribir código para seguir estas reglas y luego abstraerlo con el " ejecutar alrededor de " idioma.

Otros consejos

Me temo que hay un gran problema con el primer ejemplo, que es que si ocurre una excepción en o después de la lectura, se ejecuta el bloque finalmente . Hasta ahora tan bueno. Pero, ¿qué sucede si el fr.close () provoca otra excepción? Esto "triunfará" la primera excepción (un poco como poner return en un bloque finalmente ) y perderá toda la información sobre lo que realmente causó el problema para comenzar .

Su bloque finalmente debería usar:

IOUtil.closeSilently(fr);

donde este método de utilidad simplemente hace:

public static void closeSilently(Closeable c) {
    try { c.close(); } catch (Exception e) {} 
} 

Prefiero el segundo. ¿Por qué? Si tanto read () como close () lanzan excepciones, uno de ellos podría perderse. En la primera construcción, la excepción de close () anula la excepción de read () , mientras que en la segunda, la excepción de close () se maneja por separado.


A partir de Java 7, el try-with- la construcción de recursos lo hace mucho más simple. Para leer sin preocuparse por las excepciones:

try (FileReader fr = new FileReader("SomeFile.txt")) {
    fr.read();
    // no need to close since the try-with-resources statement closes it automatically
}

Con manejo de excepciones:

try (FileReader fr = new FileReader("SomeFile.txt")) {
    fr.read();
    // no need to close since the try-with-resources statement closes it automatically
} catch (IOException e) {
    // Do exception handling
    log(e);
    // If this catch block is run, the FileReader has already been closed.
    // The exception could have come from either read() or close();
    // if both threw exceptions (or if multiple resources were used and had to be closed)
    // then only one exception is thrown and the others are suppressed
    // but can still be retrieved:
    Throwable[] suppressed = e.getSuppressed(); // can be an empty array
    for (Throwable t : suppressed) {
        log(suppressed[t]);
    }
}

Solo se necesita un try-catch y todas las excepciones se pueden manejar con seguridad. Todavía puede agregar un bloque finalmente si lo desea, pero no es necesario cerrar los recursos.

Si tanto leer como cerrar arrojan una excepción, la excepción de leer se ocultará en la opción 1. Entonces, la segunda opción más manejo de errores.

Sin embargo, en la mayoría de los casos, la primera opción seguirá siendo preferida.

  1. En muchos casos, no puede manejar las excepciones en el método en que se generan, pero aún así debe encapsular el manejo de la secuencia dentro de esa operación.
  2. Intente agregar un escritor al código y vea cuán detallado es el segundo enfoque.

Si necesita pasar todas las excepciones generadas, se puede hacer .

La diferencia, hasta donde puedo ver, es que hay diferentes excepciones y causas en juego en diferentes niveles, y el

captura (Excepción e)

oscurece eso. El único punto de los niveles múltiples es distinguir sus excepciones y lo que hará al respecto:

try
{
  try{
   ...
  }
   catch(IOException e)
  {
  ..
  }
}
catch(Exception e)
{
  // we could read, but now something else is broken 
  ...
}

Normalmente hago lo siguiente. Primero, defina una clase basada en un método de plantilla para tratar el desastre try / catch

import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public abstract class AutoFileCloser {
    private static final Closeable NEW_FILE = new Closeable() {
        public void close() throws IOException {
            // do nothing
        }
    };

    // 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>();

    // mark a new file
    protected void newFile() {
        closeables_.add(0, NEW_FILE);
    }

    // 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 void watch(Closeable closeable) {
        closeables_.add(0, 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
            boolean skip = false;
            for (Closeable closeable : closeables_) {
                if (closeable == NEW_FILE) {
                    skip = false;
                } else  if (!skip && closeable != null) {
                    try {
                        closeable.close();
                        // don't try to re-close nested closeables
                        skip = true;
                    } 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 el " pendiente " excepción: esto se ocupa del caso en el que una excepción lanzada durante el cierre enmascararía una excepción que realmente podría interesarnos.

Finalmente, intenta cerrar desde el exterior de cualquier secuencia decorada primero, por lo que si tenía un BufferedWriter envolviendo un FileWriter, intentamos cerrar el BuffereredWriter primero, y si eso falla, aún intente cerrar el FileWriter.

Puede usar 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 = null;
            BufferedReader bufferedReader = null;
            watch(fileReader = new FileReader("somefile"));
            watch(bufferedReader = new BufferedReader(fileReader));

            // ... do something with bufferedReader

            // if you need more than one reader or writer
            newFile(); // puts a flag in the 
            FileWriter fileWriter = null;
            BufferedWriter bufferedWriter = null;
            watch(fileWriter = new FileWriter("someOtherFile"));
            watch(bufferedWriter = new BufferedWriter(fileWriter));

            // ... do something with bufferedWriter
        }
    };

    // .. other logic, maybe more AutoFileClosers

} catch (RuntimeException e) {
    // report or log the exception
}

Usando este enfoque, nunca tendrá que preocuparse por el intento / captura / finalmente para tratar de cerrar los archivos nuevamente.

Si esto es demasiado pesado para su uso, al menos piense en seguir el try / catch y el " pendiente " enfoque variable que utiliza.

La convención estándar que uso es que no debes permitir que las excepciones escapen de un bloque finalmente.

Esto se debe a que si una excepción ya se está propagando, la excepción expulsada del bloque finalmente prevalecerá sobre la excepción original (y por lo tanto se perderá).

En el 99% de los casos, esto no es lo que desea, ya que la excepción original es probablemente la fuente de su problema (cualquier excepción secundaria puede ser un efecto secundario del primero, pero oscurecerá su capacidad para encontrar la fuente de la excepción original y por lo tanto, el verdadero problema).

Entonces su código básico debería verse así:

try
{
    // Code
}
// Exception handling
finally
{
    // Exception handling that is garanteed not to throw.
    try
    {
         // Exception handling that may throw.
    }
    // Optional Exception handling that should not throw
    finally()
    {}
}

Segundo enfoque.

De lo contrario, no te veo atrapar la excepción del constructor FileReader

http://java.sun.com/j2se/1.5.0/docs/api/java/io/FileReader.html#FileReader (java.lang.String)

public FileReader (String fileName)            lanza FileNotFoundException

Entonces, generalmente tengo el constructor dentro del bloque try también. el bloque finalmente verifica si el lector NO es nulo antes de intentar cerrar.

El mismo patrón se aplica a Datasource, Connection, Statement, ResultSet.

A veces el try-catch anidado no es una preferencia, considere esto:

try{
 string s = File.Open("myfile").ReadToEnd(); // my file has a bunch of numbers
 // I want to get a total of the numbers 
 int total = 0;
 foreach(string line in s.split("\r\n")){
   try{ 
     total += int.Parse(line); 
   } catch{}
 }
catch{}

Este es probablemente un mal ejemplo, pero hay veces que necesitará try-cactch anidado.

Me gusta el enfoque de @Chris Marshall, pero nunca me gusta ver que las excepciones se traguen en silencio. Creo que es mejor registrar excepciones, especialmente si continúas de todos modos.

Siempre uso una clase de utilidad para manejar este tipo de excepciones comunes, pero haría que esta pequeña diferencia a su respuesta.

Siempre usaría un registrador (log4j para mí) para registrar errores, etc.

IOUtil.close(fr);

Una ligera modificación al método de utilidad:

public static void close(Closeable c) {
    try {
      c.close();
    } catch (Exception e) {
      logger.error("An error occurred while closing. Continuing regardless", e); 
    } 
}

En algunos casos, un Try-Catch anidado es inevitable. Por ejemplo, cuando el código de recuperación de error en sí mismo puede arrojar una excepción. Pero para mejorar la legibilidad del código, siempre puede extraer el bloque anidado en un método propio. Consulte esta publicación de blog para obtener más ejemplos sobre Try anidado Bloques de captura y captura.

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