O que é uma exceção
Uma exceção é a maneira que o Java tem de dizer "algo deu errado e não consigo continuar normalmente". Em vez de retornar um valor ruim ou corromper seus dados silenciosamente, o ambiente de execução cria um objeto de exceção descrevendo o problema e o lança. A execução normal para nesse ponto, e o Java começa a procurar código que saiba como lidar com a situação.
Se ninguém a tratar, a exceção chega ao topo do seu programa, a JVM imprime um rastreamento de pilha e o processo encerra com um status diferente de zero.
Repare que "after" nunca é impresso. No instante em que numbers[5] é avaliado, uma ArrayIndexOutOfBoundsException é lançada e o restante de main é abandonado.
Lendo um rastreamento de pilha
Quando uma exceção fica sem tratamento, você recebe uma saída como esta. Parece intimidante, mas é apenas uma lista:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Main.divide(Main.java:8)
at Main.main(Main.java:4)
Leia de cima para baixo:
- A primeira linha é o tipo (
ArithmeticException) e uma mensagem (/ by zero). - Cada linha
at ...é um quadro de pilha. O de cima é onde a exceção foi de fato lançada -dividena linha 8. Abaixo dele está o chamador,mainna linha 4.
A primeira linha at é onde você olha primeiro. Os quadros abaixo dela respondem "como chegamos até aqui?".
Execute isto e o rastreamento aponta direto para a linha a / b, depois mostra main como o chamador. Essa cadeia de chamadas é a ferramenta de depuração mais útil que o Java oferece de graça.
A hierarquia de exceções
Toda exceção é um objeto, e todas descendem de Throwable. Os dois ramos que importam:
Error- problemas graves que a JVM levanta, comoOutOfMemoryErrorouStackOverflowError. Em geral você não os captura.Exception- problemas que seu programa pode razoavelmente prever e tratar. Dentro dela ficaRuntimeException, a mãe dos erros do dia a dia, comoNullPointerException.
Throwable
├── Error (don't catch: OutOfMemoryError, StackOverflowError)
└── Exception
├── IOException (checked)
├── SQLException (checked)
└── RuntimeException (unchecked)
├── NullPointerException
├── ArithmeticException
└── ArrayIndexOutOfBoundsException
Como são classes reais, uma exceção pode carregar uma mensagem e você pode perguntar a ela sobre si mesma:
getMessage() retorna o texto após os dois pontos no rastreamento de pilha; getClass().getSimpleName() dá o tipo da exceção pelo nome.
Verificadas versus não verificadas
Esta é a distinção que mais confunde os iniciantes.
- Exceções não verificadas estendem
RuntimeException. Geralmente significam um erro no seu código - umnullque você não esperava, um índice ruim, uma divisão por zero. O compilador não obriga você a tratá-las. - Exceções verificadas estendem
Exception, mas nãoRuntimeException(por exemplo,IOException). Representam condições fora do seu controle - um arquivo ausente, uma conexão de rede que caiu. O compilador obriga você a capturá-las ou declará-las.
Se um método pode lançar uma exceção verificada, ele precisa dizer isso com throws, e todo chamador precisa lidar com ela:
Remova throws Exception de main e o código nem sequer compila - é o compilador impondo o contrato das verificadas. Exceções não verificadas nunca exigem isso.
Lançando as suas próprias
Você não só reage às exceções - você pode levantá-las. Use throw com um novo objeto de exceção para sinalizar que um argumento ou estado é inválido. Isso é muito melhor do que retornar um valor mágico como -1 e torcer para que o chamador o verifique.
Inclua sempre uma mensagem que explique o que deu errado e, idealmente, o valor problemático - o seu eu do futuro lendo o rastreamento de pilha vai agradecer. IllegalArgumentException e IllegalStateException são as duas que você mais usará ao validar entradas.
Armadilhas comuns
- Engolir exceções. Capturar uma exceção e não fazer nada (um bloco vazio) esconde bugs. No mínimo, registre-a; normalmente você deve tratá-la ou relançá-la.
- Capturar
Exceptionde forma ampla demais. Capturar aExceptionbase (ou pior,Throwable) pode mascarar problemas que você não pretendia tratar. Capture o tipo específico que você espera. - Ler o rastreamento de baixo para cima. O quadro mais relevante é a linha
atde cima, não a de baixo. Comece por ela. - Confundir
ErrorcomException. Não tente se recuperar deOutOfMemoryErrorouStackOverflowError; corrija a causa raiz.
Próximo: try-catch
Agora você sabe o que são as exceções e como lê-las. A próxima página aborda como realmente tratá-las: envolver código arriscado em um bloco try, recuperar-se no catch e executar código de limpeza no finally para que seu programa continue rodando em vez de quebrar.
Perguntas frequentes
O que é uma exceção em Java?
Uma exceção é um objeto que representa um problema detectado enquanto um programa é executado - como dividir por zero, acessar um índice além do final de um array ou chamar um método em null. Quando o problema ocorre, o Java lança a exceção: ele interrompe o fluxo normal e procura por código capaz de tratá-la. Se nada a tratar, o programa imprime um rastreamento de pilha e encerra.
Qual é a diferença entre uma exceção verificada e uma não verificada em Java?
Exceções verificadas (subclasses de Exception, mas não de RuntimeException, por exemplo IOException) precisam ser capturadas ou declaradas com throws - o compilador impõe isso. Exceções não verificadas (subclasses de RuntimeException, por exemplo NullPointerException, ArrayIndexOutOfBoundsException) geralmente indicam erros de programação e não exigem declaração. Error (como OutOfMemoryError) também é não verificada e, em geral, não deve ser capturada.
Como leio um rastreamento de pilha em Java?
Leia de cima para baixo. A primeira linha nomeia o tipo da exceção e a mensagem (por exemplo, java.lang.ArithmeticException: / by zero). Cada linha at ... abaixo é um quadro de pilha que mostra o método, o arquivo e o número da linha, começando por onde a exceção foi lançada e voltando pelos chamadores. A primeira linha at quase sempre é o lugar para olhar primeiro.