¿Es una mala práctica atrapar a Throwable?

¿Es una mala práctica atrapar a Throwable ?

Por ejemplo algo como esto:

 try { // Some code } catch(Throwable e) { // handle the exception } 

¿Es esta una mala práctica o deberíamos ser lo más específicos posible?

Debes ser lo más específico posible. De lo contrario, los errores imprevistos podrían escabullirse de esta manera.

Además, Throwable cubre el Error y generalmente no es un punto de retorno . No desea detectar / manejar eso, quiere que su progtwig muera inmediatamente para que pueda arreglarlo correctamente.

Esta es una mala idea. De hecho, incluso la captura de Exception suele ser una mala idea. Consideremos un ejemplo:

 try { inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() ); } catch(Throwable e) { inputNumber = 10; //Default, user did not enter valid number } 

Ahora, digamos que getUserInput () bloquea por un tiempo, y otro hilo detiene el hilo de la peor manera posible (llama a thread.stop ()). Tu bloque catch capturará un error ThreadDeath . Esto es súper malo. El comportamiento de su código después de atrapar esa Excepción es en gran parte indefinido.

Un problema similar ocurre con la captura de Excepción. Tal vez getUserInput() falló debido a una excepción de interrupción, o una excepción de permiso denegado al tratar de registrar los resultados, o todo tipo de otras fallas. No tiene idea de qué fue lo que salió mal, debido a eso, tampoco tiene idea de cómo solucionar el problema.

Tienes tres mejores opciones:

1 – Capture exactamente la (s) excepción (es) que sabe cómo manejar:

 try { inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() ); } catch(ParseException e) { inputNumber = 10; //Default, user did not enter valid number } 

2 – Retome cualquier excepción que encuentre y no sepa cómo manejarla:

 try { doSomethingMysterious(); } catch(Exception e) { log.error("Oh man, something bad and mysterious happened",e); throw e; } 

3 – Usa un bloque finally para que no tengas que recordar volver a lanzar:

  Resources r = null; try { r = allocateSomeResources(); doSomething(r); } finally { if(r!=null) cleanUpResources(r); } 

También tenga en cuenta que cuando atrapa Throwable , también puede detectar InterruptedException que requiere un tratamiento especial. Consulte Cómo abordar la excepción interrumpida para obtener más detalles.

Si solo desea capturar excepciones no verificadas, también podría considerar este patrón

 try { ... } catch (RuntimeException exception) { //do something } catch (Error error) { //do something } 

De esta forma, cuando modifique su código y agregue una llamada a método que pueda generar una excepción marcada, el comstackdor se lo recordará y luego podrá decidir qué hacer para este caso.

No es una mala práctica si no puedes tener una burbuja de excepción de un método.

Es una mala práctica si realmente no puede manejar la excepción. Es mejor agregar “tiradas” a la firma del método que simplemente atrapar y volver a lanzar o, lo que es peor, envolverlo en una RuntimeException y volver a lanzar.

directamente desde el javadoc de la clase Error (que recomienda no detectar estos):

  * An Error is a subclass of Throwable * that indicates serious problems that a reasonable application * should not try to catch. Most such errors are abnormal conditions. * The ThreadDeath error, though a "normal" condition, * is also a subclass of Error because most applications * should not try to catch it. * A method is not required to declare in its throws * clause any subclasses of Error that might be thrown * during the execution of the method but not caught, since these * errors are abnormal conditions that should never occur. * * @author Frank Yellin * @version %I%, %G% * @see java.lang.ThreadDeath * @since JDK1.0 

Catching Throwable a veces es necesario si está utilizando bibliotecas que arrojan errores con demasiado entusiasmo, de lo contrario, su biblioteca puede matar su aplicación.

Sin embargo, sería mejor en estas circunstancias especificar solo los errores específicos arrojados por la biblioteca, en lugar de todos los Throwables.

Throwable es la clase base para todas las clases que puede arrojarse (no solo excepciones). Es poco lo que puede hacer si detecta un OutOfMemoryError o KernelError (consulte cuándo atrapar java.lang.Error? )

capturar excepciones debería ser suficiente.

depende de tu lógica o de ser más específico para tus opciones / posibilidades. Si hay alguna excepción específica sobre la que pueda reactjsr de forma significativa, podría atraparla primero y hacerlo.

Si no lo hay y está seguro de que hará lo mismo con todas las excepciones y errores (por ejemplo, salir con un mensaje de error), entonces no es problema atrapar el objeto arrojado.

Por lo general, el primer caso se cumple y no se detectaría el lanzamiento. Pero todavía hay muchos casos en los que la captura funciona bien.

Aunque se describe como una práctica muy mala, a veces puede encontrar casos raros que no solo sean útiles sino también obligatorios. Aquí hay dos ejemplos.

En una aplicación web donde debe mostrar una página de significado de error completo al usuario. Este código se asegura de que esto ocurra, ya que es una gran try/catch todos sus servidores de solicitudes (servlets, acciones de struts o cualquier controlador ….)

 try{ //run the code which handles user request. }catch(Throwable ex){ LOG.error("Exception was thrown: {}", ex); //redirect request to a error page. } 

}

Como otro ejemplo, considere que tiene una clase de servicio que sirve negocios de transferencia de fondos. Este método devuelve un TransferReceipt si la transferencia se realiza o NULL si no puede.

 String FoundtransferService.doTransfer( fundtransferVO); 

Ahora, al obtener imágenes, obtiene una List de transferencias de fondos del usuario y debe usar el servicio anterior para hacerlas todas.

 for(FundTransferVO fundTransferVO : fundTransferVOList){ FoundtransferService.doTransfer( foundtransferVO); } 

¿Pero qué pasará si ocurre alguna excepción? No debe detenerse, ya que una transferencia puede haber sido exitosa y otra no, debe continuar pasando por toda la List usuarios y mostrar el resultado de cada transferencia. Entonces terminas con este código.

 for(FundTransferVO fundTransferVO : fundTransferVOList){ FoundtransferService.doTransfer( foundtransferVO); }catch(Throwable ex){ LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex); } } 

Puede navegar por muchos proyectos de código abierto para ver que el throwable está realmente en caché y manejado. Por ejemplo, aquí hay una búsqueda de tomcat , struts2 y struts2 :

https://github.com/apache/tomcat/search?utf8=%E2%9C%93&q=catch%28Throwable https://github.com/apache/struts/search?utf8=%E2%9C%93&q=catch % 28Throwable https://github.com/primefaces/primefaces/search?utf8=%E2%9C%93&q=catch%28Throwable

Si bien en general es una mala práctica atrapar a Throwable (como lo aclararon las numerosas respuestas sobre esta pregunta), los escenarios donde capturar a Throwable son útiles son bastante comunes. Permítanme explicar uno de esos casos que uso en mi trabajo, con un ejemplo simplificado.

Considere un método que realiza la adición de dos números, y después de la adición exitosa, envía una alerta por correo electrónico a ciertas personas. Supongamos que el número devuelto es importante y lo utiliza el método de llamada.

 public Integer addNumbers(Integer a, Integer b) { Integer c = a + b; //This will throw a NullPointerException if either //a or b are set to a null value by the //calling method successfulAdditionAlert(c); return c; } private void successfulAdditionAlert(Integer c) { try { //Code here to read configurations and send email alerts. } catch (Throwable e) { //Code to log any exception that occurs during email dispatch } } 

El código para enviar alertas por correo electrónico lee muchas configuraciones del sistema y, por lo tanto, puede haber una variedad de excepciones lanzadas desde ese bloque de código. Pero no queremos que ninguna excepción encontrada durante el envío de alertas se propague al método de llamador, ya que ese método simplemente se refiere a la sum de los dos valores de Entero que proporciona. Por lo tanto, el código para enviar alertas por correo electrónico se coloca en un bloque try-catch , donde Throwable es capturado y cualquier excepción simplemente se registra, permitiendo que el rest del flujo continúe.

Si utilizamos throwable , también cubre Error y eso es todo.

Ejemplo.

  public class ExceptionTest { /** * @param args */ public static void m1() { int i = 10; int j = 0; try { int k = i / j; System.out.println(k); } catch (Throwable th) { th.printStackTrace(); } } public static void main(String[] args) { m1(); } 

}

Salida:

 java.lang.ArithmeticException: / by zero at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12) at com.infy.test.ExceptionTest.main(ExceptionTest.java:25) 

Throwable es la superclase de todos los errores y excedentes. Si usa Throwable en una cláusula catch, no solo capturará todas las excepciones, sino que también detectará todos los errores. La JVM produce errores para indicar problemas graves que no están destinados a ser manejados por una aplicación. Ejemplos típicos para eso son OutOfMemoryError o StackOverflowError. Ambos son causados ​​por situaciones que están fuera del control de la aplicación y no se pueden manejar. Por lo tanto, no deberías atrapar objetos Throwables a menos que tengas la plena seguridad de que solo será una excepción dentro de Throwable.

La pregunta es un poco vaga; ¿Estás preguntando “¿está bien atrapar Throwable “, o “¿está bien atrapar un Throwable y no hacer nada”? Mucha gente aquí respondió lo último, pero eso es un problema secundario; El 99% de las veces no debes “consumir” o descartar la excepción, ya sea que estés atrapando Throwable o IOException o lo que sea.

Si propaga la excepción, la respuesta (como la respuesta a tantas preguntas) es “depende”. Depende de lo que haga con la excepción: por qué lo está atrapando.

Un buen ejemplo de por qué quieres atrapar a Throwable es proporcionar algún tipo de limpieza si hay algún error. Por ejemplo, en JDBC, si se produce un error durante una transacción, le conviene deshacer la transacción:

 try { … } catch(final Throwable throwable) { connection.rollback(); throw throwable; } 

Tenga en cuenta que la excepción no se descarta, sino que se propaga.

Pero como política general, atrapar a Throwable porque no tienes un motivo y eres demasiado flojo para ver qué excepciones están pasando es de mala forma y una mala idea.

En general, quiere evitar la captura de Error , pero puedo pensar en (al menos) dos casos específicos en los que es apropiado hacerlo:

  • Desea cerrar la aplicación en respuesta a errores, especialmente a AssertionError que de lo contrario es inofensivo.
  • ¿Está implementando un mecanismo de agrupación de subprocesos similar a ExecutorService.submit () que requiere que reenvíe las excepciones al usuario para que puedan manejarlo?