Introdução
Uma exceção é um evento de erro que pode ocorrer durante a execução de um programa e interromper seu fluxo normal. O Java fornece uma maneira robusta e orientada a objetos de lidar com cenários de exceção conhecida como Manipulação de Exceção em Java.
Exceções em Java podem surgir de diferentes tipos de situações, como dados incorretos inseridos pelo usuário, falha de hardware, falha na conexão de rede ou um servidor de banco de dados fora do ar. O código que especifica o que fazer em cenários de exceção específicos é chamado de tratamento de exceção.
Lançando e Capturando Exceções
O Java cria um objeto de exceção quando ocorre um erro durante a execução de uma instrução. O objeto de exceção contém muitas informações de depuração, como hierarquia de método, número da linha onde a exceção ocorreu e tipo de exceção.
Se ocorrer uma exceção em um método, o processo de criar o objeto de exceção e entregá-lo ao ambiente de execução é chamado “lançar a exceção”. O fluxo normal do programa é interrompido e o Ambiente de Execução Java (JRE) tenta encontrar o manipulador para a exceção. O Manipulador de Exceção é o bloco de código que pode processar o objeto de exceção.
- A lógica para encontrar o manipulador de exceção começa com a busca no método onde o erro ocorreu.
- Se nenhum manipulador apropriado for encontrado, então ele se moverá para o método chamador.
- E assim por diante.
Portanto, se a pilha de chamadas do método for A->B->C
e uma exceção for gerada no método C
, então a busca pelo manipulador apropriado se moverá de C->B->A
.
Se um manipulador de exceção apropriado for encontrado, o objeto de exceção é passado para o manipulador para processá-lo. Diz-se que o manipulador está “capturando a exceção”. Se não houver um manipulador de exceção apropriado, o programa é encerrado e informações sobre a exceção são impressas no console.
O framework de tratamento de exceções em Java é usado apenas para lidar com erros em tempo de execução. Os erros em tempo de compilação devem ser corrigidos pelo desenvolvedor que escreve o código, caso contrário, o programa não será executado.
Palavras-chave de Manipulação de Exceção em Java
Java fornece palavras-chave específicas para fins de tratamento de exceções.
- throw – Sabemos que se ocorrer um erro, um objeto de exceção é criado e então o tempo de execução do Java começa a processar para lidar com eles. Às vezes, podemos querer gerar exceções explicitamente em nosso código. Por exemplo, em um programa de autenticação de usuário, devemos lançar exceções aos clientes se a senha for
null
. A palavra-chavethrow
é usada para lançar exceções ao tempo de execução para que ele as trate. - throws – Quando estamos lançando uma exceção em um método e não a estamos tratando, então temos que usar a palavra-chave
throws
na assinatura do método para informar o programa chamador as exceções que podem ser lançadas pelo método. O método chamador pode tratar essas exceções ou propagá-las para seu método chamador usando a palavra-chavethrows
. Podemos fornecer múltiplas exceções na cláusulathrows
, e ela pode ser usada com o métodomain()
também. - try-catch – Usamos o bloco
try-catch
para tratamento de exceções em nosso código.try
é o início do bloco ecatch
está no final do blocotry
para tratar as exceções. Podemos ter múltiplos blocoscatch
com um blocotry
. O blocotry-catch
também pode ser aninhado. O blococatch
requer um parâmetro que deve ser do tipoException
. - finalmente – o bloco
finally
é opcional e pode ser usado apenas com um blocotry-catch
. Como a exceção interrompe o processo de execução, podemos ter alguns recursos abertos que não serão fechados, então podemos usar o blocofinally
. O blocofinally
sempre é executado, quer ocorra uma exceção ou não.
Um Exemplo de Tratamento de Exceção
- O método
testException()
está lançando exceções usando a palavra-chavethrow
. A assinatura do método usa a palavra-chavethrows
para informar ao chamador o tipo de exceções que ele pode lançar. - No método
main()
, estou tratando exceções usando o blocotry-catch
no métodomain()
. Quando não estou tratando, estou propagando para tempo de execução com a cláusulathrows
no métodomain()
. - O
testException(-10)
nunca é executado por causa da exceção e então o blocofinally
é executado.
O método printStackTrace()
é um dos métodos úteis na classe Exception
para fins de depuração.
Este código irá produzir o seguinte:
Outputjava.io.FileNotFoundException: Negative Integer -5
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:24)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:10)
Releasing resources
Exception in thread "main" java.io.IOException: Only supported for index 0 to 10
at com.journaldev.exceptions.ExceptionHandling.testException(ExceptionHandling.java:27)
at com.journaldev.exceptions.ExceptionHandling.main(ExceptionHandling.java:19)
Alguns pontos importantes a serem observados:
- Não podemos ter uma cláusula
catch
oufinally
sem uma declaraçãotry
. - A
try
statement should have eithercatch
block orfinally
block, it can have both blocks. - Não podemos escrever nenhum código entre blocos
try-catch-finally
. - Podemos ter vários blocos
catch
com uma única instruçãotry
. - Os blocos
try-catch
podem ser aninhados de forma semelhante às instruçõesif-else
. - Podemos ter apenas um bloco
finally
com uma instruçãotry-catch
.
Hierarquia de Exceções em Java
Conforme mencionado anteriormente, quando uma exceção é gerada, um objeto de exceção é criado. As exceções em Java são hierárquicas e a herança é usada para categorizar diferentes tipos de exceções. Throwable
é a classe pai da hierarquia de exceções em Java e possui dois objetos filhos – Error
e Exception
. As Exception
s são ainda divididas em Exception
s Verificadas e Exception
s de Tempo de Execução.
- Erros:
Error
s são cenários excepcionais que estão fora do escopo da aplicação, e não é possível antecipar e recuperar deles. Por exemplo, falha de hardware, falha na máquina virtual Java (JVM) ou erro de falta de memória. É por isso que temos uma hierarquia separada deError
s e não devemos tentar lidar com essas situações. Alguns dosError
s comuns sãoOutOfMemoryError
eStackOverflowError
. - Exceções Verificadas: Exceções verificadas são cenários excepcionais que podemos antecipar em um programa e tentar recuperar. Por exemplo,
FileNotFoundException
. Devemos capturar essa exceção e fornecer uma mensagem útil ao usuário e registrá-la adequadamente para fins de depuração. AException
é a classe pai de todas as Exceções Verificadas. Se estivermos lançando uma Exceção Verificada, devemos capturá-la no mesmo método, ou precisamos propagá-la para o chamador usando a palavra-chavethrows
. - Exceção de Tempo de Execução: Exceções de tempo de execução são causadas por programação inadequada. Por exemplo, tentar recuperar um elemento de uma matriz. Devemos verificar o comprimento da matriz antes de tentar recuperar o elemento, caso contrário, pode lançar
ArrayIndexOutOfBoundsException
em tempo de execução.RuntimeException
é a classe pai de todas as Exceções de Tempo de Execução. Se estivermosthrow
ando qualquer Exceção de Tempo de Execução em um método, não é necessário especificá-las na cláusulathrows
da assinatura do método. Exceções de tempo de execução podem ser evitadas com uma programação mais cuidadosa.
Alguns métodos úteis das Classes de Exceção
Java Exception
e todas as suas subclasses não fornecem métodos específicos, e todos os métodos são definidos na classe base – Throwable
. As classes de Exception
são criadas para especificar diferentes tipos de cenários de Exception
para que possamos identificar facilmente a causa raiz e lidar com a Exception
de acordo com seu tipo. A classe Throwable
implementa a interface Serializable
para interoperabilidade.
Alguns dos métodos úteis da classe Throwable
são:
- public String getMessage() – Este método retorna a mensagem
String
deThrowable
e a mensagem pode ser fornecida ao criar a exceção por meio de seu construtor. - public String getLocalizedMessage() – Este método é fornecido para que subclasses possam substituí-lo para fornecer uma mensagem específica para a localidade ao programa chamador. A implementação da classe
Throwable
deste método usa o métodogetMessage()
para retornar a mensagem de exceção. - public synchronized Throwable getCause() – Este método retorna a causa da exceção ou
null
se a causa for desconhecida. - public String toString() – Este método retorna as informações sobre
Throwable
em formatoString
, aString
retornada contém o nome da classeThrowable
e a mensagem localizada. - public void printStackTrace() – Este método imprime as informações da pilha de execução no fluxo de erro padrão, este método é sobrecarregado e podemos passar
PrintStream
ouPrintWriter
como argumento para escrever as informações da pilha de execução no arquivo ou fluxo.
Java 7 Gerenciamento Automático de Recursos e melhorias no bloco catch
Se você está catch
ando muitas exceções em um único bloco try
, você notará que o código do bloco catch
consiste principalmente em código redundante para registrar o erro. No Java 7, uma das características foi um bloco catch
melhorado onde podemos capturar várias exceções em um único bloco catch
. Aqui está um exemplo do bloco catch
com esse recurso:
Há algumas restrições, como o objeto de exceção ser final e não podermos modificá-lo dentro do bloco catch
, leia a análise completa em Melhorias no Bloco Catch do Java 7.
Na maioria das vezes, usamos o bloco finally
apenas para fechar os recursos. Às vezes, esquecemos de fechá-los e recebemos exceções de tempo de execução quando os recursos estão esgotados. Essas exceções são difíceis de depurar, e pode ser necessário examinar cada local onde estamos usando esse recurso para garantir que estamos fechando-o. No Java 7, uma das melhorias foi o try-with-resources
, onde podemos criar um recurso na própria declaração try
e usá-lo dentro do bloco try-catch
. Quando a execução sai do bloco try-catch
, o ambiente de tempo de execução fecha automaticamente esses recursos. Aqui está um exemplo do bloco try-catch
com essa melhoria:
A Custom Exception Class Example
O Java fornece muitas classes de exceção para usarmos, mas às vezes podemos precisar criar nossas próprias classes de exceção personalizadas. Por exemplo, para notificar o chamador sobre um tipo específico de exceção com a mensagem apropriada. Podemos ter campos personalizados para rastreamento, como códigos de erro. Por exemplo, digamos que escrevemos um método para processar apenas arquivos de texto, então podemos fornecer ao chamador o código de erro apropriado quando outro tipo de arquivo é enviado como entrada.
Primeiro, crie MyException
:
Depois, crie um CustomExceptionExample
:
Podemos ter um método separado para processar diferentes tipos de códigos de erro que obtemos de diferentes métodos. Alguns deles são consumidos porque talvez não queiramos notificar o usuário disso, ou alguns deles serão lançados de volta para notificar o usuário do problema.
Aqui estou estendendo Exception
para que, sempre que essa exceção for lançada, ela deva ser tratada no método ou retornada ao programa chamador. Se estendermos RuntimeException
, não há necessidade de especificá-lo na cláusula throws
.
Essa foi uma decisão de design. O uso de Exception
s verificadas tem a vantagem de auxiliar os desenvolvedores a entender quais exceções podem ser esperadas e tomar as medidas apropriadas para lidar com elas.
Melhores Práticas para Tratamento de Exceções em Java
- Use Exceções Específicas – As classes base da hierarquia de exceções não fornecem informações úteis, por isso o Java possui várias classes de exceção, como
IOException
com sub-classes adicionais comoFileNotFoundException
,EOFException
, etc. Devemos sempre lançar e capturar classes de exceção específicas para que o chamador saiba a causa raiz da exceção facilmente e as processe. Isso facilita a depuração e ajuda as aplicações cliente a lidar com exceções adequadamente. - Lance cedo ou falhe rapidamente – Devemos tentar lançar exceções o mais cedo possível. Considere o método
processFile()
mencionado acima, se passarmos o argumentonull
para esse método, receberemos a seguinte exceção:
OutputException in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:134)
at java.io.FileInputStream.<init>(FileInputStream.java:97)
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:42)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
Ao depurar, teremos que observar cuidadosamente o rastreamento da pilha para identificar a localização real da exceção. Se alterarmos nossa lógica de implementação para verificar essas exceções mais cedo, como abaixo:
Então, o rastreamento da pilha da exceção indicará onde a exceção ocorreu com uma mensagem clara:
Outputcom.journaldev.exceptions.MyException: File name can't be null
at com.journaldev.exceptions.CustomExceptionExample.processFile(CustomExceptionExample.java:37)
at com.journaldev.exceptions.CustomExceptionExample.main(CustomExceptionExample.java:12)
- Captura Tardia – Como o Java obriga a lidar com a exceção verificada ou declará-la na assinatura do método, às vezes os desenvolvedores tendem a
capturar
a exceção e registrar o erro. Mas essa prática é prejudicial porque o programa chamador não recebe notificação da exceção. Devemoscapturar
exceções apenas quando podemos lidar com elas adequadamente. Por exemplo, no método acima, estoulançando
exceções de volta para o método chamador para lidar com ela. O mesmo método poderia ser usado por outras aplicações que talvez queiram processar a exceção de maneira diferente. Ao implementar qualquer recurso, devemos semprelançar
exceções de volta para o chamador e permitir que eles decidam como lidar com ela. - Fechamento de Recursos – Como as exceções interrompem o processamento do programa, devemos fechar todos os recursos no bloco finally ou usar o aprimoramento try-with-resources do Java 7 para permitir que o tempo de execução do Java o feche para você.
- Registrando Exceções – Sempre devemos registrar mensagens de exceção e ao
lançar
exceções fornecer uma mensagem clara para que o chamador saiba facilmente por que a exceção ocorreu. Devemos sempre evitar um blococatch
vazio que apenas consome a exceção e não fornece detalhes significativos da exceção para depuração. - Bloco catch único para várias exceções – Na maioria das vezes, registramos detalhes da exceção e fornecemos uma mensagem ao usuário, neste caso, devemos usar o recurso do Java 7 para lidar com várias exceções em um único bloco
catch
. Esta abordagem reduzirá o tamanho do nosso código e também parecerá mais limpa. - Usando Exceções Personalizadas – É sempre melhor definir uma estratégia de tratamento de exceção no momento do design e, em vez de
lançar
ecapturar
várias exceções, podemos criar uma exceção personalizada com um código de erro, e o programa chamador pode lidar com esses códigos de erro. Também é uma boa ideia criar um método utilitário para processar diferentes códigos de erro e usá-los. - Convenções de Nomenclatura e Empacotamento – Ao criar sua exceção personalizada, certifique-se de que ela termine com
Exception
para que fique claro pelo nome em si que é uma classe de exceção. Além disso, certifique-se de empacotá-las como é feito no Kit de Desenvolvimento Java (JDK). Por exemplo,IOException
é a exceção base para todas as operações de IO. - Use Exceções com Cautela – As exceções são custosas e às vezes não é necessário lançar exceções de todo, podemos retornar uma variável booleana para o programa chamador indicar se uma operação foi bem-sucedida ou não. Isso é útil quando a operação é opcional e você não quer que seu programa fique parado por falha. Por exemplo, ao atualizar as cotações de estoque no banco de dados a partir de um serviço da web de terceiros, podemos querer evitar lançar exceções se a conexão falhar.
- Documente as Exceções Lançadas – Use Javadoc
@throws
para especificar claramente as exceções lançadas pelo método. Isso é muito útil quando você está fornecendo uma interface para outras aplicações usarem.
Conclusão
Neste artigo, você aprendeu sobre o tratamento de exceções em Java. Você aprendeu sobre throw
e throws
. Você também aprendeu sobre os blocos try
(e try-with-resources
), catch
e finally
.
Source:
https://www.digitalocean.com/community/tutorials/exception-handling-in-java