Pergunta

É uma má prática capturar Throwable?

Por exemplo algo assim:

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

Esta é uma má prática ou devemos ser o mais específicos possível?

Foi útil?

Solução

Você precisa ser o mais específico possível.Caso contrário, bugs imprevistos podem surgir dessa forma.

Além do mais, Throwable capas Error também e isso é geralmente não há ponto de retorno.Você não quer capturar/lidar com isso, você quer que seu programa morra imediatamente para que você possa corrigi-lo corretamente.

Outras dicas

Esta é uma má ideia.Na verdade, mesmo pegando 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 outro thread interrompa seu thread da pior maneira possível (ele chama thread.stop() ).Seu bloco catch irá capturar um ThreadDeath Erro.Isso é muito ruim.O comportamento do seu código após capturar essa exceção é amplamente indefinido.

Um problema semelhante ocorre ao capturar Exception.Talvez getUserInput() falhou devido a uma InterruptException ou a uma exceção de permissão negada ao tentar registrar os resultados ou a todos os tipos de outras falhas.Você não tem ideia do que deu errado, pois por isso também não tem ideia de como resolver o problema.

Você tem três opções melhores:

1 -- Capture exatamente a(s) Exceção(ões) que você sabe tratar:

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

2 - Relacione qualquer exceção que você encontrar e não saiba como lidar:

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

3 - Use um bloco final para não precisar se lembrar de relançar:

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

Também esteja ciente de que quando você pegar Throwable, você também pode pegar InterruptedException que requer um tratamento especial.Ver Lidando com InterruptedException para mais detalhes.

Se você quiser apenas capturar exceções não verificadas, você 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 bolha de exceção em um método.

É uma má prática se você realmente não consegue lidar com a exceção.É melhor adicionar "lançamentos" à assinatura do método do que apenas capturar e lançar novamente ou, pior, envolvê-lo em uma RuntimeException e lançar novamente.

Catching Throwable às vezes é necessário se você estiver usando bibliotecas que lançam erros com excesso de entusiasmo, caso contrário, sua biblioteca pode matar seu aplicativo.

No entanto, seria melhor nestas 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ê possa fazer se detectar um OutOfMemoryError ou KernelError (veja Quando capturar java.lang.Error?)

capturar exceções deve ser suficiente.

depende da sua lógica ou para ser mais específico das suas opções/possibilidades.Se houver alguma exceção específica à qual você possa reagir de maneira significativa, você poderá capturá-la primeiro e fazê-lo.

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), não será problema capturar o que pode ser lançado.

Normalmente, o primeiro caso é válido e você não pegaria o que pode ser jogado.Mas ainda há muitos casos em que capturá-lo funciona bem.

Embora seja descrita como uma prática muito ruim, às vezes você pode encontrar cru casos em que não é apenas ú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 e significativa ao usuário.Este código garante que isso aconteça, pois é um grande try/catch em 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 negócios de transferência de fundos.Este método retorna um TransferReceipt se a transferência for feita ou NULL se não pudesse.

String FoundtransferService.doTransfer( fundtransferVO);

Agora imaginando você obtém um List 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 qualquer exceção acontece?Você não deve parar, pois uma transferência pode ter sido bem-sucedida e outra não, você deve continuar através de todos os usuários List, e mostre o resultado para cada transferência.Então você acaba com esse 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 por vários projetos de código aberto para ver se o throwable é realmente armazenado em cache e manipulado.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

A questão é um pouco vaga;você está perguntando "está tudo bem pegar Throwable", ou "está tudo bem pegar um Throwable e não fazer nada"?Muitas pessoas aqui responderam a última opção, mas essa é uma questão secundária;99% das vezes você não deve “consumir” ou descartar a exceção, esteja você capturando Throwable ou IOException como queiras.

Se você propagar a exceção, a resposta (como a resposta para tantas perguntas) será “depende”.Depende do que você está fazendo com a exceção – por que você a está detectando.

Um bom exemplo de por que você gostaria de capturar Throwable é fornecer algum tipo de limpeza se houver algum erro.Por exemplo, em JDBC, se ocorrer um erro durante uma transação, você desejará 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 política geral, capturar 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 má forma e uma má ideia.

De modo geral, você deseja evitar pegar Errors, mas posso pensar em (pelo menos) dois casos específicos em que é apropriado fazer isso:

  • Você deseja encerrar o aplicativo em resposta a erros, especialmente AssertionError que de outra forma é inofensivo.
  • Você está implementando um mecanismo de pooling de threads semelhante ao ExecutorService.submit() isso exige que você encaminhe exceções de volta ao usuário para que ele possa lidar com isso.

Se usarmos jogável, então cobre Erro também 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();
}

}

Saída:

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 de Throwable.

Embora geralmente seja uma má prática capturar Throwable (conforme elucidado pelas inúmeras respostas a esta pergunta), os cenários em que capturar Throwable Throwable é útil são bastante comuns.Deixe-me explicar um caso que utilizo 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 determinadas 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 por e-mail lê muitas configurações do sistema e, portanto, pode haver uma variedade de exceções lançadas nesse bloco de código.Mas não queremos que nenhuma exceção encontrada durante o envio do alerta se propague para o método chamador, pois esse método está simplesmente preocupado com a soma dos dois valores Inteiros que ele fornece.Portanto, o código para enviar alertas por 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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top