Pregunta

¿Existe una manera elegante de manejar las excepciones que se generan en el bloque finally?

Por ejemplo:

try {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
finally {
   try{
     resource.close();
   }
   catch( Exception ex ) {
     // Could not close the resource?
   }
}

¿Cómo evita el try / catch en el bloque <=>?

¿Fue útil?

Solución

Normalmente lo hago así:

try {
  // Use the resource.
} catch( Exception ex ) {
  // Problem with the resource.
} finally {
  // Put away the resource.
  closeQuietly( resource );
}

En otro lugar:

protected void closeQuietly( Resource resource ) {
  try {
    if (resource != null) {
      resource.close();
    }
  } catch( Exception ex ) {
    log( "Exception during Resource.close()", ex );
  }
}

Otros consejos

Normalmente uso uno de los closeQuietly métodos en org.apache.commons.io.IOUtils:

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}

Si está utilizando Java 7 y resource implementa AutoClosable, puede hacerlo (usando InputStream como ejemplo):

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}

Podría decirse que es un poco exagerado, pero puede ser útil si está dejando que las excepciones broten y no pueda registrar nada desde su método (por ejemplo, porque es una biblioteca y prefiere dejar que el código de llamada maneje excepciones y registro):

Resource resource = null;
boolean isSuccess = false;
try {
    resource = Resource.create();
    resource.use();
    // Following line will only run if nothing above threw an exception.
    isSuccess = true;
} finally {
    if (resource != null) {
        if (isSuccess) {
            // let close throw the exception so it isn't swallowed.
            resource.close();
        } else {
            try {
                resource.close();
            } catch (ResourceException ignore) {
                // Just swallow this one because you don't want it 
                // to replace the one that came first (thrown above).
            }
        }
    }
}

ACTUALIZACIÓN: Investigué esto un poco más y encontré una gran publicación de blog de alguien que claramente ha pensado en esto más que yo: http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of -stream.html Va un paso más allá y combina las dos excepciones en una sola, lo que podría ser útil en algunos casos.

A partir de Java 7 ya no necesita cerrar explícitamente los recursos en un bloque finalmente , sino que puede usar la sintaxis probar -with-resources. La declaración try-with-resources es una declaración try que declara uno o más recursos. Un recurso es un objeto que debe cerrarse después de que el programa haya terminado con él. La declaración de prueba con recursos garantiza que cada recurso se cierre al final de la declaración. Cualquier objeto que implemente java.lang.AutoCloseable, que incluye todos los objetos que implementan java.io.Closeable, puede usarse como recurso.

Asuma el siguiente código:

try( Connection con = null;
     Statement stmt = con.createStatement();
     Result rs= stmt.executeQuery(QUERY);)
{  
     count = rs.getInt(1);
}

Si ocurre alguna excepción, se llamará al método cerrar en cada uno de estos tres recursos en el orden opuesto en el que se crearon. Significa que el método de cierre se llamaría primero para ResultSetm, luego la instrucción y al final para el objeto Connection.

También es importante saber que se suprimen las excepciones que se producen cuando se llama automáticamente a los métodos de cierre. Estas excepciones suprimidas se pueden recuperar mediante el método getsuppressed () definido en la clase Throwable .

Fuente: https://docs.oracle.com/javase/ tutorial / esencial / excepciones / tryResourceClose.html

Ignorar las excepciones que ocurren en un bloque 'finalmente' es generalmente una mala idea a menos que uno sepa cuáles serán esas excepciones y qué condiciones representarán. En el patrón de uso normal try/finally, el bloque try coloca las cosas en un estado que el código externo no esperará, y el bloque finally restaura el estado de esas cosas a lo que el código externo espera. El código externo que detecta una excepción generalmente esperará que, a pesar de la excepción, todo se haya restaurado a un estado normal. Por ejemplo, supongamos que un código inicia una transacción y luego intenta agregar dos registros; el " finalmente " el bloque realiza una " retrotracción si no se confirma " operación. Una persona que llama podría estar preparada para que ocurra una excepción durante la ejecución de la segunda & "; Agregue &"; operación, y puede esperar que si detecta una excepción de este tipo, la base de datos estará en el estado que tenía antes de intentar cualquiera de las operaciones. Sin embargo, si se produce una segunda excepción durante la reversión, podrían ocurrir cosas malas si la persona que llama hace alguna suposición sobre el estado de la base de datos. La falla de reversión representa una crisis importante , una que no debe ser atrapada por el código esperando un mero & "; No se pudo agregar el registro &"; excepción.

Mi inclinación personal sería tener un método para capturar excepciones que ocurran y envolverlas en una & "; CleanupFailedException &"; reconociendo que tal falla representa un problema importante y que dicha excepción no debe ser detectada a la ligera.

Una solución, si las dos Excepciones son dos clases diferentes

try {
    ...
    }
catch(package1.Exception err)
   {
    ...
   }
catch(package2.Exception err)
   {
   ...
   }
finally
  {
  }

Pero a veces no puedes evitar este segundo try-catch. p.ej. para cerrar una secuencia

InputStream in=null;
try
 {
 in= new FileInputStream("File.txt");
 (..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
 }
catch(SQLException err)
 {
 //handle exception
 }
finally
 {
 //at the end, we close the file
 if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
 }

¿Por qué quieres evitar el bloqueo adicional? Como el bloque finalmente contiene & Quot; normal & Quot; operaciones que pueden generar una excepción Y desea que el bloque finalmente se ejecute completamente TIENE que detectar excepciones.

Si no espera que el bloque finalmente arroje una excepción y no sabe cómo manejar la excepción de todos modos (simplemente volcaría el seguimiento de la pila) deje que la excepción aumente la pila de llamadas (elimine el try-catch del bloque finalmente).

Si desea reducir la escritura, puede implementar un " global " bloque externo try-catch, que detectará todas las excepciones lanzadas finalmente en bloques:

try {
    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }
} catch (Exception ex) {
    ...
}

Después de mucha consideración, creo que el siguiente código es el mejor:

MyResource resource = null;
try {
    resource = new MyResource();
    resource.doSomethingFancy();
    resource.close(); 
    resource = null;  
} finally {
    closeQuietly(resource)
}

void closeQuietly(MyResource a) {
    if (a!=null)
        try {
             a.close();
        } catch (Exception e) {
             //ignore
        }
}

Ese código garantiza lo siguiente:

  1. El recurso se libera cuando finaliza el código
  2. Las excepciones lanzadas al cerrar el recurso no se consumen sin procesarlas.
  3. El código no intenta cerrar el recurso dos veces, no se creará ninguna excepción innecesaria.

Si puedes, debes probar para evitar la condición de error para empezar.

try{...}
catch(NullArgumentException nae){...}
finally
{
  //or if resource had some useful function that tells you its open use that
  if (resource != null) 
  {
      resource.Close();
      resource = null;//just to be explicit about it was closed
  }
}

Además, probablemente solo deberías detectar excepciones de las que puedas recuperarte, si no puedes recuperarte, deja que se propague al nivel superior de tu programa. Si no puede probar una condición de error, tendrá que rodear su código con un bloque de captura de prueba como ya lo ha hecho (aunque recomendaría aún detectar errores esperados específicos).

Podría refactorizar esto en otro método ...

public void RealDoSuff()
{
   try
   { DoStuff(); }
   catch
   { // resource.close failed or something really weird is going on 
     // like an OutOfMemoryException 
   }
}

private void DoStuff() 
{
  try 
  {}
  catch
  {
  }
  finally 
  {
    if (resource != null) 
    {
      resource.close(); 
    }
  }
}

Normalmente hago esto:

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}

Justificación: si he terminado con el recurso y el único problema que tengo es cerrarlo, no hay mucho que pueda hacer al respecto. Tampoco tiene sentido matar todo el hilo si de todos modos he terminado con el recurso.

Este es uno de los casos cuando, al menos para mí, es seguro ignorar esa excepción marcada.

Hasta el día de hoy no he tenido ningún problema con este idioma.

try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

Trabajo realizado. No hay pruebas nulas. Captura única, incluye adquirir y liberar excepciones. Por supuesto, puede usar el idioma Execute Around y solo tiene que escribirlo una vez para cada tipo de recurso.

Cambiar Resource de mejor respuesta a Closeable

Streams implementa <=> Por lo tanto, puede reutilizar el método para todas las secuencias

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top