¿Es una mala práctica atrapar 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?
¿Es una mala práctica atrapar 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?
Respuestas:
Debe ser lo más específico posible. De lo contrario, los errores imprevistos podrían desaparecer de esta manera.
Además, también Throwable
cubre Error
y eso no suele ser un punto de retorno . No quiere captar / manejar eso, quiere que su programa muera inmediatamente para que pueda solucionarlo correctamente.
Esta es una mala idea. De hecho, incluso atrapar 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 () 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 ThreadDeath
error. 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);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Esto se registrará en una buena excepción con 10 causas.
También tenga en cuenta que cuando lo atrape Throwable
, también puede atrapar, lo InterruptedException
que 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.
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
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.
Throwable
instancias, por ejemplo, para el registro de excepciones personalizado.
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.
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.
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.
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/catch
todos 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 TransferReceipt
si se realiza la transferencia o NULL
si no se pudo.
String FoundtransferService.doTransfer( fundtransferVO);
Ahora la imagen obtiene una cantidad 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 con todos los usuarios List
y 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 throwable
realmente se almacena en caché y se maneja. Por ejemplo, aquí es una búsqueda de tomcat
, struts2
y 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
throwable
, de eso se trata esta pregunta
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 Throwable
o IOException
lo 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 Throwable
es 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 Throwable
porque 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.
En términos generales, desea evitar la captura de mensajes de correo Error
electrónico, pero puedo pensar en (al menos) dos casos específicos en los que es apropiado hacerlo:
AssertionError
si son inofensivos.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)
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.
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-catch
bloque, donde Throwable
se captura y las excepciones simplemente se registran, lo que permite que continúe el resto del flujo.
Exception
s por todos los medios, pero no Throwable
.