¿Es una mala práctica atrapar Throwable?

Respuestas:

104

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

Además, también Throwablecubre Errory eso no suele ser un punto de retorno . No quiere captar / manejar eso, quiere que su programa muera inmediatamente para que pueda solucionarlo correctamente.

BalusC
fuente
38
Hay situaciones en las que es apropiado detectar Error y continuar. Por ejemplo: en un servlet, si encuentra un OutOfMemoryError porque una solicitud específica se comió toda la memoria, puede intentar continuar ya que los objetos serán GC después de que se maneje la solicitud. Lo mismo ocurre con un error de afirmación. No cierra una aplicación porque algo salió mal en una solicitud.
gawi
7
¿Cómo sabe qué se asignó y qué no antes de la OOME? Todas las apuestas están canceladas una vez que obtenga eso, incluso dentro de un contenedor J2EE como Tomcat o JBoss.
bmauter
10
Hemos tenido NoSuchMethodError y afortunadamente no eliminamos a todos nuestros clientes cerrando el servidor como sucedió dos semanas después de la implementación. es decir. Siempre tenemos un catchall atrapando Throwable y haciendo el mejor esfuerzo para manejar y enviar el error al cliente. Después de todo, hay muchos tipos de errores que se pueden recuperar, ya que solo pueden afectar a 1 de cada 1000 clientes.
Dean Hiller
10
" quieres que tu programa muera inmediatamente para que puedas arreglarlo correctamente " => si tu programa muere, ¿cómo sabes qué pasó? Capturar Throwable / Error para registrar el problema es algo razonable que hacer ...
assylias
3
@assylias Una aplicación independiente arrojará un error fatal a stderr.
Philip Whitehouse
36

Esta es una mala idea. De hecho, incluso atrapar Exceptionsuele 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 () se bloquea por un tiempo, y otro hilo detiene su hilo de la peor manera posible (llama thread.stop ()). Su bloque de captura detectará un ThreadDeatherror. Esto es muy malo. El comportamiento de su código después de detectar esa excepción es en gran parte indefinido.

Se produce un problema similar al detectar la excepción. Tal vez getUserInput()falló debido a una InterruptException, o una excepción de permiso denegado al intentar registrar los resultados, o todo tipo de otras fallas. No tiene idea de qué salió mal, ya que debido a eso, tampoco tiene idea de cómo solucionar el problema.

Tienes tres opciones mejores:

1 - Detecte exactamente las excepciones que sabe cómo manejar:

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

2 - Vuelva a lanzar cualquier excepción con la que se encuentre y no sepa cómo manejar:

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

3 - Use un bloque de finalmente para que no tenga que acordarse de volver a lanzar:

 Resources r = null;
 try {
      r = allocateSomeResources();
      doSomething(r);
 } finally {
     if(r!=null) cleanUpResources(r);
 }
Brandon Yarbrough
fuente
4
+1 por afirmar que incluso atrapar Exception no es bueno. Al menos hay una ThreadInterruptedException que requiere un cuidado especial (en resumen, después de detectarlo, debe establecer el estado interrumpido del hilo de nuevo en 'verdadero')
Kirill Gamazkov
Sé que es solo para ilustrar sus palabras, pero creo que puede verificar con una expresión regular si su entrada de usuario es alfanumérica o qué formato necesita y no use try catch en todas partes cada vez.
amdev
¿Cómo se supone que voy a saber si hay un error / arrojable si no lo atrapo? No vi nada en los registros. Aplicación Java EE. Pasé días atascados sin saber cuál era el problema hasta que agregué esta captura.
Philip Rego
2
Considero la opción # 2 como una mala práctica. Imagínese 10 llamadas encadenadas con registro y repetición. Si miras el archivo de registro, no estarás contento. La excepción se registra 10 veces, lo que hace que los registros sean muy difíciles de leer. En mi humilde opinión, es mucho mejor hacer throw new Exception("Some additional info, eg. userId " + userId, e);. Esto se registrará en una buena excepción con 10 causas.
Petr Újezdský
21

También tenga en cuenta que cuando lo atrape Throwable, también puede atrapar, lo InterruptedExceptionque requiere un tratamiento especial. Consulte Manejo de InterruptedException para obtener más detalles.

Si solo desea detectar excepciones no marcadas, también puede considerar este patrón

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

De esta manera, cuando modifica su código y agrega una llamada a un método que puede generar una excepción marcada, el compilador se lo recordará y luego podrá decidir qué hacer en este caso.

gawi
fuente
14

directamente del javadoc de la clase Error (que recomienda no atraparlos):

 * An <code>Error</code> is a subclass of <code>Throwable</code> 
 * that indicates serious problems that a reasonable application 
 * should not try to catch. Most such errors are abnormal conditions. 
 * The <code>ThreadDeath</code> error, though a "normal" condition,
 * is also a subclass of <code>Error</code> because most applications
 * should not try to catch it. 

 * A method is not required to declare in its <code>throws</code> 
 * clause any subclasses of <code>Error</code> 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
Andrew Norman
fuente
13

No es una mala práctica si absolutamente no puede 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 "lanzamientos" a la firma del método que simplemente capturar y volver a lanzar o, peor aún, envolverlo en una RuntimeException y volver a lanzarlo.

duffymo
fuente
10
Totalmente de acuerdo: hay casos absolutamente legítimos para manejar todas las Throwableinstancias, por ejemplo, para el registro de excepciones personalizado.
Yuriy Nakonechnyy
9

La captura de Throwable a veces es necesaria 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 lanzados por la biblioteca, en lugar de todos los Throwables.

ADN
fuente
12
¿O usa una biblioteca mejor escrita?
Raedwald
6
De hecho, si tiene la opción ;-)
DNA
Ese es el mayor problema de atrapar objetos arrojadizos y volver a lanzarlos. Realmente hace una interfaz imposible para todos los métodos de la pila. Tienen que lidiar con un lanzamiento o tener una firma de lanzamiento inutilizable con la que otros tendrán que lidiar.
Andrew Norman
6

Throwable es la clase base para todas las clases que se pueden lanzar (no solo excepciones). Es poco lo que puede hacer si detecta un OutOfMemoryError o KernelError (consulte ¿ Cuándo detectar java.lang.Error? )

la captura de Excepciones debería ser suficiente.

ic3
fuente
5

depende de su lógica o de ser más específico a sus opciones / posibilidades. Si hay alguna excepción específica sobre la que posiblemente pueda reaccionar de una manera significativa, puede detectarla primero y hacerlo.

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

Por lo general, el primer caso se sostiene y no atraparías el lanzador. Pero todavía hay muchos casos en los que detectarlo funciona bien.

b.buchhold
fuente
4

Aunque se describe como una práctica muy mala, a veces puede encontrar casos raros que no solo son útiles sino también obligatorios. A continuación se muestran dos ejemplos.

En una aplicación web en la que debe mostrar al usuario una página de error completa con significado. Este código se asegura de que esto suceda, ya que es importante para try/catchtodos los manejadores 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 para negocios de transferencia de fondos. Este método devuelve TransferReceiptsi se realiza la transferencia o NULLsi no se pudo.

String FoundtransferService.doTransfer( fundtransferVO);

Ahora la imagen obtiene una cantidad Listde 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 con todos los usuarios Listy mostrar el resultado a 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 explorar muchos proyectos de código abierto para ver que throwablerealmente se almacena en caché y se maneja. Por ejemplo, aquí es una búsqueda de tomcat, struts2y primefaces:

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

Alireza Fattahi
fuente
1
Vio el código en estos enlaces. ¡Lanzable no es solo el que se atrapa! También hay otras excepciones detectadas antes de Throwable.
developer1011
@ developer101 por supuesto, pero captan throwable, de eso se trata esta pregunta
Alireza Fattahi
4

La pregunta es un poco vaga; ¿Estás preguntando "está bien atrapar Throwable" o "está bien atrapar unThrowable y no hacer nada"? Mucha gente aquí respondió lo último, pero ese es un tema secundario; El 99% de las veces no se debería "consumir" o descartar la excepción, si tienes que coger Throwableo IOExceptionlo que sea.

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

Un buen ejemplo de por qué querría capturar Throwablees proporcionar algún tipo de limpieza si hay algún error. Por ejemplo, en JDBC, si ocurre un error durante una transacción, querrá revertir 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, detectar Throwableporque no tiene una razón y es demasiado vago para ver qué excepciones específicas se están lanzando es una mala idea y una mala idea.

Garret Wilson
fuente
1

En términos generales, desea evitar la captura de mensajes de correo Errorelectrónico, 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 AssertionError si son inofensivos.
  • ¿Está implementando un mecanismo de agrupación de subprocesos similar a ExecutorService.submit () que requiere que reenvíe las excepciones al usuario para que pueda manejarlo?
Gili
fuente
0

Si usamos throwable , entonces 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)
VicXj
fuente
0

Throwable es la superclase de todos los errores y excesos. Si usa Throwable en una cláusula catch, no solo detectará todas las excepciones, sino que también detectará todos los errores. La JVM genera errores para indicar problemas graves que no están destinados a ser manejados por una aplicación. Ejemplos típicos de 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 debe capturar Throwables a menos que esté bastante seguro de que solo será una excepción dentro de Throwable.

Sidath Bhanuka Randeniya
fuente
-1

Si bien en general es una mala práctica atrapar Throwable (como se aclara por las numerosas respuestas a esta pregunta), los escenarios en los que atrapar Throwable es útil son bastante comunes. Permítanme explicarles uno de esos casos que utilizo en mi trabajo, con un ejemplo simplificado.

Considere un método que realiza la suma de dos números y, después de la suma exitosa, envía una alerta por correo electrónico a ciertas personas. Suponga 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, podría 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 la persona que llama, ya que ese método simplemente se ocupa de la suma de los dos valores enteros que proporciona. Por lo tanto, el código para enviar alertas por correo electrónico se coloca en un try-catchbloque, donde Throwablese captura y las excepciones simplemente se registran, lo que permite que continúe el resto del flujo.

CodeNewbie
fuente
Intentaría evitar esto teniendo un hilo dedicado al trabajo (con una cola) de enviar correos electrónicos.
boumbh
Sugeriría que este sigue siendo un código incorrecto. Atrapa Exceptions por todos los medios, pero no Throwable.
Andrewf