Exceções em Java
A linguagem Java possui um mecanismo especial para o tratamento de erros que possam ocorrer em tempo de execução do programa. Diferentemente de outras linguagens, o surgimento de um erro ocasiona a interrupção imediata do programa, porém em Java podemos tratar esta situação de erro de uma forma adequada e evitando, assim, a interrupção do programa.
Uma exceção, basicamente é uma classe de Java representada na sua forma mais genérica pela classe java.lang.Exception
, logo todas as exceções que ocorram ao longo da execução do seu programa podem ser tratadas como objetos do tipo Exception
.
Uma característica importante sobre exceções é que, pelo fato delas poderem ocorrer a qualquer momento, estas são literalmente “lançadas” de volta para a cadeia de execução e chamada das classes.
Bloco try / catch
Como a exceção é lançada por toda a cadeia de classes do sistema, a qualquer momento é possível se “pegar” essa exceção e dar a ela o tratamento adequado.
Para se fazer este tratamento, é necessário pontuar que um determinado trecho de código que será observado e que uma possível exceção será tratada de uma determinada maneira, segue um exemplo deste novo bloco:
No caso acima, o bloco try
, é o trecho de código em que uma exceção é esperada e o bloco catch
, em correspondência ao bloco try
, prepara-se para “pegar” a exceção ocorrida e dar a ela o tratamento necessário. Uma vez declarado um bloco try
, a declaração do bloco catch torna-se obrigatória.
Na linha 12 o bloco catch
declara receber um objeto do tipo Exception
, lembrando do conceito da herança, todas as exceções do Java são classes filhas de Exception
.
Algo importante de ser comentado, é que quando uma exceção ocorre, as demais linhas de código deixam de ser executadas até encontrar o bloco catch que irá tratar a exceção.
Exemplo:
Observemos o exemplo acima. Neste código, caso aconteça um erro na linha 13, as linhas de 14 a 17 não seriam executadas, retornando então o código a ser executado apenas a partir da linha 19 (trecho correspondente ao bloco catch
).
Uma vez que uma exceção foi tratada por um bloco catch
, a execução do programa segue normalmente.
Caso não ocorra erro durante a execução teremos a seguinte saída no console:
Se no lugar de um número inteiro for digitado outra informação teremos a seguinte saída no console:
Palavra-chave throw
Também é possível que você próprio envie uma exceção em alguma situação especifica, como em uma situação de login em que o usuário digita incorretamente sua senha. Para realizarmos tal tarefa é necessária a utilização da palavra-chave throw
da seguinte maneira:
Vamos ver um exemplo de lançamento de uma exceção do tipo Exception
:
Note que, assim que a palavra-chave throw
for utilizada, podemos tentar tratar a exceção no bloco try
/ catch
ou lançar essa exceção nesse método, caso a exceção em questão seja do tipo checked. Observe também que a palavra reservada new
foi utilizada, visto que a exceção é um novo objeto que deve ser criado na memória. Isso se faz necessário para que a exceção possa ser lançada por toda a pilha de execução até que seja devidamente tratada ou acarrete no término da aplicação.
Quando executamos este código, temos a seguinte saída no console, note que no primeiro teste entramos com uma senha invalida abc
, portanto foi lançado a exceção, no segundo teste entramos com a senha valida 123456
e executamos a aplicação por completo.
Alguns métodos importantes da classe Exception
:
printStackTrace()
- Imprime em tela a pilha de execução. Muito comum para auxiliar no diagnóstico de erros.
getMessage()
- Retorna uma String com a mensagem contida na exceção.
getClass()
- Retorna uma String com o nome complete da classe da exceção.
Bloco finnaly
A palavra-chave finally
representa um trecho de código que será sempre executado, independentemente se uma exceção ocorrer. Por exemplo:
No exemplo anterior, a mensagem “Bloco Final!”, sempre será exibida, ocorrendo ou não um Exception
.
Caso não ocorra erro durante a execução teremos a seguinte saída no console:
Se digitarmos zero no valor do divisor será lançado uma exceção, note que o bloco finally
será executado mesmo que ocorra alguma exceção:
O bloco finally
é muito utilizado quando queremos liberar algum recurso, como: uma conexão com o banco de dados, um arquivo de dados, etc. Veremos mais adiante alguns códigos que fazem uso do finally
para este propósito.
Palavra-chave throws
Caso em algum método precise lançar uma exceção, mas você não deseja tratá-la, quer retorna-la para o objeto que fez a chamada ao método que lançou a exceção, basta utilizar a palavra chave throws
no final da assinatura do método.
Quando utilizamos o throws
precisamos também informar qual ou quais exceções podem ser lançadas.
Exemplo:
Note que na linha 29 declaramos na assinatura do método que ele pode ou não lançar uma exceção do tipo java.lang.Exception
.
E o método public static void main(String[] args)
será responsável por tratar alguma exceção que o método dividir(double dividendo, double divisor)
possa lançar.
Caso não ocorra erro durante a execução teremos a seguinte saída no console:
Se digitarmos zero no valor do divisor será lançado uma exceção pelo método dividir()
, esta exceção será tratada pelo método main()
:
Hierarquia das Exceptions
A princípio, Java possui diversos tipos específicos de exceção, cada um deles diretamente relacionado a uma operação específica, por este motivo para que uma classe seja considerada uma exceção é imprescindível que ela de alguma forma seja filha de java.lang.Exception
.
As exceções que são subclasses de java.lang.Exception
são normalmente divididas em duas parte as Checked e Unchecked, que explicaremos mais adiante.
No quadro abaixo, temos um exemplo da hierarquia de algumas subclasses de Exception. Uma das subclasses mais comuns é a java.lang.RuntimeException
, sobretudo para os usuários finais, pois como podem acontecer em qualquer parte do código durante sua execução, normalmente, quando não tratadas ocasionam na interrupção do programa.
Checked Exceptions
As checked exceptions, são exceções já previstas pelo compilador. Usualmente são geradas pela palavra chave throw
(que é discutido mais adiante). Como ela é prevista já em tempo de compilação, se faz necessária a utilização do bloco try
/ catch
ou da palavra chave throws
.
A princípio, todas as classes filhas de Exception são do tipo checked, exceto pelas subclasses de java.lang.RuntimeException
(exceção em tempo de execução).
Unchecked Exceptions
Um dos fatores que tornam a RuntimeException
e suas classes filhas tão específicas em relação as demais subclasses de Exception
é que elas são exceções não diretamente previstas por acontecer em tempo de execução, ou seja, são unchecked exceptions. Por exemplo:
Observe que no trecho de código acima, temos uma variável do tipo int
recebendo uma entrada do usuário também do tipo int
. Porém, vamos supor que nosso usuário não digite um inteiro, mas sim um caractere.
Este tipo de exceção, que acontece somente em tempo de execução, a princípio não era tratado e uma exceção do tipo java.util.InputMismatchException
será gerada e nosso programa é encerrado. Agora iremos prever este erro, utilizando o bloco try
/ catch
:
Desta forma, a digitação incorreta do usuário será tratada e uma mensagem de erro será exibida caso uma exceção aconteça, a variável validado não receberia o valor true.
No vídeo a seguir mostramos passo a passo como funciona exceções no Java:
Errors
Errors são um tipo especial de Exception que representam erros da JVM, tais como estouro de memória, entre outros. Para este tipo de erro normalmente não é feito tratamento, pois sempre quando um java.lang.Error
ocorre a execução do programa é interrompida.
Tratando múltiplas Exceções
É possível se tratar múltiplos tipos de exceção dentro do mesmo bloco try
/ catch
, para tal, basta declara-los um abaixo do outro, assim como segue:
As exceções tratadas pelos catchs devem seguir a ordem da mais especifica para a menos especifica.
No vídeo a seguir mostramos passo a passo como tratar múltiplas exceções no Java:
Criando sua exceção
Na linguagem Java podemos também criar nossas próprias exceções, normalmente fazemos isso para garantir que nossos métodos funcionem corretamente, dessa forma podemos lançar exceções com mensagens de fácil entendimento pelo usuários, ou que possa facilitar o entendimento do problema para quem estiver tentando chamar seu método possa tratar o erro.
No exemplo abaixo vamos criar uma exceção chamada ErroDivisao que será lançada quando ocorrer uma divisão incorreta, para isso precisamos criar esta classe filha de Exception.
Agora vamos criar a classe TesteErroDivisao, que iremos utilizar para testar o lançamento da nossa exceção ErroDivisao, crie o método restoDaDivisao que irá calcular o resto da divisão de dois números e lançara a exceção ErroDivisao caso tenha o valor do divisor maior que o valor do dividendo.
Quando executamos a classe TesteErroDivisao caso não ocorra nenhum problema será apresentado o resto da divisão, se ocorrer algum erro será apresentado a mensagem “Divisão Invalida!!!”.