Lidando com Exceptions - Blog da Caelum: desenvolvimento, web, mobile, UX e Scrum
Lidando com Exceptions
Postado dia 07/10/2006 por Fabio Kung em Programação 15
Sabemos que as exceções em Java são classificadas em dois tipos:
Checked Exceptions: seguem a regra do handle-or-declare. O desenvolvedor é obrigado a tratar (try-catch) ou relançar (throws), caso não saiba como tratar.
Unchecked Exceptions: não é obrigatório tratar nem relançar, apesar de ser possível. Caso não haja try-catch adequado à exceção gerada, ela é automaticamente relançada. Geralmente são filhas de java.lang.RuntimeException direta ou indiretamente.
Uma das grandes dificuldades dos desenvolvedores Java é saber como lidar adequadamente com exceções. Perguntas como "Minha exceção deve ser checked ou unchecked?" e "O que escrever no meu bloco catch?" são extremamente comuns. Espero esclarecer um pouco estas dúvidas expondo aqui a minha maneira de agir nestes casos.
Gerando exceções
Aconteceu algo de errado no seu sistema (atingiu um estado inconsistente) e você está louco para fazer throw new Exception(); ?
Primeira regra: coloque no mínimo uma mensagem explicativa na sua exceção. Quantas vezes você não ficou nervoso com o seu colega desenvolvedor, que jogou uma exceção sem mensagem nenhuma e não há como ter a mínima idéia do que aconteceu? Melhorando um pouco fica: throw new Exception("Um dos argumentos para o cálculo do desconto é Inválido");
Segunda regra: use sempre exceções específicas ao seu caso, isto é, se o problema foi no cálculo de um desconto faça algo do tipo throw new CalculoDeDescontoException("Um dos argumentos é inválido"); e não throw new Exception("Não há como calcular o desconto pois um dos argumentos é inválido");. Aqui você poderia terminar com muitas exceções específicas demais. Como alternativa, poderia então fazer algo como throw new LogicaDeNegocioException("Um dos argumentos para o calculo do desconto é inválido"). Usando sempre o bom senso.
Checked ou Unchecked?
Pare um pouco para pensar no significado de um try-catch:
Uma chance de se recuperar do erro. Uma chance de tratar a exceção.
Tendo isso em mente fica mais fácil decidir entre lançar uma checked exception ou uma unchecked exception. Tente responder a seguinte pergunta:
Quero dar a chance a quem chama meu código de tratar o possível erro?
Se a resposta for sim, use uma checked exception. Neste caso o chamador vai ser obrigado a fazer o try-catch e terá a chance de se recuperar do erro. Não é o que você tanto quis?
Caso ele não saiba como se recuperar do erro, será obrigado a relançar a exceção. Assim, outro lugar terá a chance de se recuperar do erro. No fim das contas, o que uma checked exception trouxe foi a capacidade de recuperação.
Terceira regra: use checked exceptions para erros recuperáveis.
E quanto a erros irrecuperáveis? Nestes casos não queremos dar a chance de recuperação do erro. São casos como "queimou o banco de dados", ou então NullPointerException, que é sempre culpa do programador (apesar do nosso orgulho dizer o contrário). Deu erro e pronto, não há o que fazer. A única saída é mostrar para o usuário do sistema que aconteceu um problema, uma mensagem do tipo:
Desculpe, ocorreu um erro interno. Contate o administrador.
Como para unchecked exceptions não é obrigatório o handle-or-declare o mais comum é que a exceção vá subindo na pilha de chamadas e seja exibida ao usuário. Aqui uma grande vantagem é que pode existir um componente especialista em lidar com errors irrecuperáveis. Ele poderia interceptar todas as ações do sistema e esperar por erros que ninguém tratou: erros irrecuperáveis. Quando um erro destes chegar, o componente poderia fazer log da ocorrência, fazer rollback da transação e formatar a mensagem adequada ao usuário (não parece muito legal jogar o Stacktrace na cara de um usuário, parece?)
Implementar este componente interceptador não é tão difícil atualmente:
Ambiente WEB: recursos como Servlet Filters que interceptam todas as requisições em um ambiente web ou tratamento de erro declarativo pertencente à especificação de servlets/jsp (seção error-page do web.xml).
Ambientes desktop: programação orientada a aspectos, proxies ou coisas menos avançadas como o padrão de projeto Decorator.
Quarta regra: use unchecked exceptions para erros irrecuperáveis.
Ei, a API que eu uso não segue estas regras!
Você, como bom samaritano, agora segue as boas práticas. Mas o JDBC por exemplo, não segue. Supondo que você seja obrigado a usá-lo:
// Epa! SQLException é erro grave, irrecuperável! Pode ter queimado o banco de dados...
// Não deveria ser checked exception e, além disso, é genérica demais.
// Serve para muitos casos!
throw new AcessoADadosException("Problema na criação do Statement", e);
// Agora sim, encapsulei a SQLException na minha exceção específica.
// Além disso ela é RuntimeException: unchecked!
Neste caso, AcessoADadosException é uma unchecked exception, já que o seu erro é irrecuperável e você não quer dar a chance de ninguém se recuperar dele. Além de que, agora a sua exceção é mais específica e descreve melhor o seu problema, sua API ficou mais limpa: não vão ser mais necessários try-catch(SQLException) nem throws SQLException espalhados pelo seu sistema todo.
De fato, o caso mais comum é o de erros irrecuperáveis. Por isso essa prática de transformar checked exceptions (mal empregadas) em unchecked exceptions têm se tornado comum.
Quinta regra: nunca faça:
Este código pega muito mais erros do que ele está realmente esperando e o pior, não está preparado para tratá-los. Sempre relance erros que você não está preparado para tratar. Nunca pegue um erro e ignore-o. O rollback de uma transação pode depender da ocorrência de um erro irrecuperável que você acaba de esconder do resto do sistema!
Tags: exceptions, Java, oo
Michael Nascimento Santos 09/10/2006 at 08:03 #
Outra regra interessante: se quiser apenas logar uma exceção e não etiver usando um framework de logging, faça:
e nunca:
Pelo menos a informação do stack trace será impressa e permitirá identificar o lugar que originou o erro.
Rodrigo Soriano Arcova 13/10/2006 at 16:59 #
Muito bacana Fábio.
Rodrigo Soriano Arcova.
Paulo Silveira 17/10/2006 at 16:40 #
imprime no System.err
Um exemplo clássico (e famoso) de mal uso de exceptions está no Velocity… tente pegar ou renderizar um template. O método joga 3 exceptions , algo como: ParserException, TemplateNotFoundException e Exception.
Ahn!? Isso mesmo, Exception, você não sabe o que aconteceu dentro do método, só sabe que ocorreu uma exception.
Vinícius 12/12/2006 at 17:20 #
Ao invés de imprimir uma stacktrace no console o ideal é registrar um uncaughtexceptionhandler. Assim você pode encaminhar a exceção que não foi pega para um log.
Você pode mudar o uncaughtExceptionHandler da Thread chamando: Thread.setUncaughtExceptionHandler
ou mudar o da aplicação chamando o método
Thread.setDefaultUncaughtExceptionHandler.
Você terá que implementar a interface Thread.UncaughtExceptionHandler que define um método muito simples, que é chamado toda vez que uma runtime exception não é capturada. Nesse método sim, dê uma mensagem bem trabalhada para seu usuário e grave o feio stack trace da Thread em algum log.
Antonio 28/05/2007 at 14:41 #
Fiz o teste da fj31 e não entendi porque: 1) a questão 2 tem como resposta a opção c e não a opção a. 2) porque o código da questão 5 não compila e o código da questão 7 compila.
Daniel Destro 03/07/2009 at 07:46 #
Um artigo muito bom que vale a leitura sobre tratamento de exceções:
http://tutorials.jenkov.com/java-exception-handling/index.html
JuuGO(Programdor) 06/09/2011 at 14:22 #
Otima a Postagem Explicativo e Pratico para os programadores que estão iniciando ou até mesmo programadores experientes.
Marcelo Labbati 11/04/2012 at 17:42 #
Gostei muito do tutorial. Quando vi a primeira vez este lance de unchekedException nao estava conseguindo abstrair este conceito de "Ter uma chance de tratar a excessão". Agora ficou claro.
Paulo 31/05/2012 at 15:05 #
Para o caso da …
throw new AcessoADadosException("Problema na criação do Statement", e);
foi criada uma classe para isso?
Maicon Herverton 02/04/2013 at 11:21 #
public static String getStack(Throwable exception) {
return (sw.toString());
Eduardo 18/01/2017 at 10:04 #
A minha dúvida em relação exception é a seguinte. Ao criar a minha própria classe para tratar uma exceção, por exemplo ValidationException:
public class ValidationException extends Exception{
public ValidationException(String msg) {
public ValidationException(Exception e) {
super(e.getMessage());
public ValidationException(String msg, Exception e) {
No construtor onde eu só recebo a Exception, se eu não chamar o e.getMessage, lá na minha página aparecerá o nome do pacote e classe junto com a mensagem de erro assim:
br.edu.sistemaCadastro.exception.ValidationException: Campo Senha Obrigatório!
Campo Usuário Obrigatório!
Se puderem me explicar isso de uma maneira simples eu agradeço.
Renato 09/09/2017 at 20:12 #
#Eduardo terá que fazer esta mesma pergunta no fórum do guj pois é voltado especificamente para isso.