É uma prática ruim de pegar Throwable
?
Por exemplo, algo assim:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Isso é uma má prática ou devemos ser o mais específicos possível?
É uma prática ruim de pegar Throwable
?
Por exemplo, algo assim:
try {
// Some code
} catch(Throwable e) {
// handle the exception
}
Isso é uma má prática ou devemos ser o mais específicos possível?
Respostas:
Você precisa ser o mais específico possível. Caso contrário, bugs imprevistos podem se espalhar dessa maneira.
Além disso, Throwable
cobre Error
também e isso geralmente não tem ponto de retorno . Você não quer pegar / lidar com isso, você quer que seu programa morra imediatamente para que você possa corrigi-lo adequadamente.
Esta é uma má ideia. Na verdade, mesmo pegar Exception
é geralmente uma má ideia. Vamos considerar um exemplo:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(Throwable e) {
inputNumber = 10; //Default, user did not enter valid number
}
Agora, digamos que getUserInput () bloqueie por um tempo e outra thread interrompa sua thread da pior maneira possível (ela chama thread.stop ()). Seu bloco catch irá pegar um ThreadDeath
erro. Isso é muito ruim. O comportamento do seu código após capturar essa exceção é amplamente indefinido.
Um problema semelhante ocorre com a captura de exceção. Talvez tenha getUserInput()
falhado devido a uma InterruptException ou uma exceção de permissão negada ao tentar registrar os resultados, ou todos os tipos de outras falhas. Você não tem ideia do que deu errado, por isso também não tem ideia de como consertar o problema.
Você tem três opções melhores:
1 - Capture exatamente a (s) exceção (ões) que você sabe como lidar:
try {
inputNumber = NumberFormat.getInstance().formatNumber( getUserInput() );
} catch(ParseException e) {
inputNumber = 10; //Default, user did not enter valid number
}
2 - Relembre qualquer exceção que você encontrar e não souber como lidar:
try {
doSomethingMysterious();
} catch(Exception e) {
log.error("Oh man, something bad and mysterious happened",e);
throw e;
}
3 - Use um bloco finally para que você não precise se lembrar de relançar:
Resources r = null;
try {
r = allocateSomeResources();
doSomething(r);
} finally {
if(r!=null) cleanUpResources(r);
}
throw new Exception("Some additional info, eg. userId " + userId, e);
. Isso será registrado em uma boa exceção com 10 causas.
Também esteja ciente de que quando você pega Throwable
, você também pode pegar o InterruptedException
que requer um tratamento especial. Consulte Lidando com InterruptedException para obter mais detalhes.
Se você deseja apenas capturar exceções não verificadas, também pode considerar este padrão
try {
...
} catch (RuntimeException exception) {
//do something
} catch (Error error) {
//do something
}
Dessa forma, quando você modifica seu código e adiciona uma chamada de método que pode lançar uma exceção verificada, o compilador irá lembrá-lo disso e então você poderá decidir o que fazer neste caso.
direto do javadoc da classe Error (que recomenda não capturá-los):
* 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
Não é uma prática ruim se você absolutamente não pode ter uma exceção borbulhando de um método.
É uma prática ruim se você realmente não pode lidar com a exceção. Melhor adicionar "throws" à assinatura do método do que apenas pegar e relançar ou, pior, envolvê-lo em uma RuntimeException e relançar.
Throwable
instâncias - por exemplo, para registro de exceção personalizado.
Às vezes, o Catching Throwable é necessário se você estiver usando bibliotecas que geram erros com muito entusiasmo, caso contrário, sua biblioteca pode matar seu aplicativo.
No entanto, seria melhor nessas circunstâncias especificar apenas os erros específicos lançados pela biblioteca, em vez de todos os Throwables.
Throwable é a classe base para todas as classes que podem ser lançadas (não apenas exceções). Há pouco que você pode fazer se capturar um OutOfMemoryError ou KernelError (consulte Quando capturar java.lang.Error? )
Capturar exceções deve ser o suficiente.
depende da sua lógica ou para ser mais específico às suas opções / possibilidades. Se houver alguma exceção específica à qual você possa reagir de uma forma significativa, você pode pegá-la primeiro e fazer isso.
Se não houver e você tiver certeza de que fará a mesma coisa para todas as exceções e erros (por exemplo, sair com uma mensagem de erro), então não será problema pegar o que pode ser jogado.
Normalmente, o primeiro caso se mantém e você não pegaria o que pode ser jogado. Mas ainda existem muitos casos em que capturá-lo funciona bem.
Embora seja descrito como uma prática muito ruim, às vezes você pode encontrar casos raros em que não só é útil, mas também obrigatório. Aqui estão dois exemplos.
Em um aplicativo da web onde você deve mostrar uma página de erro completa significativa para o usuário. Este código garante que isso aconteça, pois é muito grande em try/catch
torno de todos os seus manipuladores de solicitação (servlets, ações de struts ou qualquer 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 outro exemplo, considere que você tem uma classe de serviço que atende a negócios de transferência de fundos. Este método retorna um TransferReceipt
se a transferência foi feita ou NULL
não.
String FoundtransferService.doTransfer( fundtransferVO);
Agora, imaginando que você obtenha uma List
série de transferências de fundos do usuário e você deve usar o serviço acima para fazer todas elas.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}
Mas o que acontecerá se alguma exceção acontecer? Você não deve parar, pois uma transferência pode ter sido bem-sucedida e outra não, você deve continuar com todos os usuários List
e mostrar o resultado a cada transferência. Então você acaba com este código.
for(FundTransferVO fundTransferVO : fundTransferVOList){
FoundtransferService.doTransfer( foundtransferVO);
}catch(Throwable ex){
LOG.error("The transfer for {} failed due the error {}", foundtransferVO, ex);
}
}
Você pode navegar em muitos projetos de código aberto para ver se o throwable
está realmente armazenado em cache e gerenciado. Por exemplo, aqui está uma pesquisa de tomcat
, struts2
e 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
, que é sobre o que é esta pergunta
A pergunta é um pouco vaga; você está perguntando "está tudo bem em pegar Throwable
" ou "está tudo bem em pegar um Throwable
e não fazer nada"? Muitas pessoas aqui responderam a última pergunta, mas isso é um problema secundário; 99% do tempo que você não deve "consumir" ou descartar a exceção, se você está pegando Throwable
ou IOException
ou qualquer outra coisa.
Se você propagar a exceção, a resposta (como a resposta a tantas perguntas) é "depende". Depende do que você está fazendo com a exceção - por que você está capturando.
Um bom exemplo de por que você deseja capturar Throwable
é fornecer algum tipo de limpeza se houver algum erro. Por exemplo, em JDBC, se ocorrer um erro durante uma transação, você deseja reverter a transação:
try {
…
} catch(final Throwable throwable) {
connection.rollback();
throw throwable;
}
Observe que a exceção não é descartada, mas propagada.
Mas, como regra geral, pegar Throwable
porque você não tem um motivo e está com preguiça de ver quais exceções específicas estão sendo lançadas é uma forma inadequada e uma má ideia.
De modo geral, você deseja evitar pegar Error
s, mas posso pensar em (pelo menos) dois casos específicos em que seja apropriado fazer isso:
AssertionError
que, de outra forma, são inofensivos.Se usarmos o que pode ser jogado , ele também cobre o erro e é isso.
Exemplo.
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();
}
}
Resultado:
java.lang.ArithmeticException: / by zero
at com.infy.test.ExceptionTest.m1(ExceptionTest.java:12)
at com.infy.test.ExceptionTest.main(ExceptionTest.java:25)
Throwable é a superclasse de todos os erros e exceções. Se você usar Throwable em uma cláusula catch, ele não apenas capturará todas as exceções, mas também todos os erros. Erros são lançados pela JVM para indicar problemas sérios que não devem ser tratados por um aplicativo. Exemplos típicos disso são OutOfMemoryError ou StackOverflowError. Ambos são causados por situações que estão fora do controle do aplicativo e não podem ser tratadas. Portanto, você não deve capturar Throwables a menos que esteja bastante confiante de que será apenas uma exceção dentro do Throwable.
Embora seja geralmente uma prática ruim capturar Throwable (conforme elucidado pelas inúmeras respostas a esta pergunta), os cenários em que a captura Throwable
é útil são bastante comuns. Deixe-me explicar um desses casos que uso no meu trabalho, com um exemplo simplificado.
Considere um método que realiza a adição de dois números e, após a adição bem-sucedida, envia um alerta por e-mail para certas pessoas. Suponha que o número retornado seja importante e usado pelo método de chamada.
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
}
}
O código para enviar alertas de e-mail lê muitas configurações do sistema e, portanto, pode haver uma variedade de exceções lançadas a partir desse bloco de código. Mas não queremos que nenhuma exceção encontrada durante o despacho do alerta se propague para o método do chamador, já que esse método está simplesmente preocupado com a soma dos dois valores Inteiros que ele fornece. Assim, o código para enviar alertas de e-mail é colocado em um try-catch
bloco, onde Throwable
é capturado e quaisquer exceções são meramente registradas, permitindo que o restante do fluxo continue.
Exception
tudo por todos os meios, mas não Throwable
.