Em cenários de desenvolvimento, imprevistos podem ocorrer, como a ausência de dados esperados ou a exaustão de memória durante a execução. Essas ocorrências, chamadas de exceções (Exceptions), representam desvios inesperados no fluxo normal de um programa.
Classificação das Exceções
- Exceções Verificadas (Checked Exceptions): São aquelas que o compilador exige que sejam tratadas. Geralmente, resultam de problemas externos ao controle direto do programador, como a falha ao acessar um arquivo inexistente.
- Exceções de Tempo de Execução (Runtime Exceptions): Diferente das verificadas, estas podem ser previstas e evitadas pelo programador através de lógica adequada. O compilador não as impõe como obrigatórias. Exemplos incluem erros de lógica de programação.
- Erros (Errors): Representam problemas graves que estão fora do controle do programador, como a falta de memória do sistema (StackOverflowError) ou problemas na JVM. Geralmente, levam à interrupção abrupta do programa e não são esperados em condições normais de operação.
Hierarquia de Exceções em Java
Java trata exceções como objetos, com a classe java.lang.Throwable servindo como a superclasse para todas as exceções e erros. A API do Java fornece diversas classes de exceção, divididas em duas categorias principais: Exception e Error.
Erros (Errors)
Erros são instanciados e lançados pela Máquina Virtual Java (JVM). A maioria deles está além do controle do desenvolvedor. Um exemplo comum é o VirtualMachineError, que ocorre quando a JVM não possui mais memória necessária para suas operações (OutOfMemoryError), geralmente resultando na terminação da thread. Outros erros, como NoClassDefFoundError ou LinkageError, indicam problemas durante a execução ou ligação de classes, e não são tratáveis pela aplicação.
Atenção: Erros graves costumam levar à quebra imediata do programa, exigindo atenção especial para evitá-los.
Exceções (Exceptions)
Dentro da ramificação Exception, destaca-se a subclasse RuntimeException, que engloba exceções comuns de tempo de execução:
ArrayIndexOutOfBoundsException: Acesso a índice de array fora dos limites.NullPointerException: Tentativa de usar um objeto nulo.ArithmeticException: Operação matemática inválida (ex: divisão por zero).MissingResourceException: Recurso indisponível.ClassNotFoundException: Classe não encontrada durante a execução.
Essas exceções geralmente são resultado de falhas na lógica do programa e devem ser minimizadas através de um bom design e validações.
Diferença entre Error e Expection
Error representa falhas críticas e irrecuperáveis, que a aplicação não consegue controlar. A JVM geralmente encerra a thread afetada. Exception, por outro lado, geralmente pode ser tratada pela aplicação, permitindo a recuperação ou um encerramento controlado.
Mecanismo de Tratamento de Exceções
O tratamento de exceções em Java envolve:
- Lançamento (Throwing): O ato de sinalizar que uma exceção ocorreu.
- Captura (Catching): O ato de interceptar e processar uma exceção lançada.
- Palavras-chave:
try,catch,finally,throw,throws.
package exception.demo1;
public class DemoHandler {
public static void main(String[] args) {
int numerator = 12;
int denominator = 0;
try {
// Bloco 'try' para código que pode lançar exceções
// Múltiplas capturas devem ser da mais específica para a mais genérica
System.out.println("Resultado da divisão: " + (numerator / denominator));
} catch (ArithmeticException ae) {
// Captura específica para divisão por zero
System.out.println("Erro de Aritmética: O denominador não pode ser zero.");
} catch (Exception e) {
// Captura genérica para outras exceções de 'Exception'
System.out.println("Ocorreu uma exceção genérica: " + e.getMessage());
} catch (Throwable t) {
// Captura para qualquer 'Throwable' (inclui Errors) - usar com cautela
System.out.println("Ocorreu um erro ou exceção grave: " + t.getMessage());
} finally {
// Bloco 'finally' é sempre executado, independentemente de exceções
System.out.println("Bloco finally executado - finalização.");
// Essencial para liberar recursos (arquivos, conexões)
}
System.out.println("------------------------------");
try {
new DemoHandler().performDivision(numerator, denominator);
} catch (ArithmeticException e) {
// Captura a exceção lançada pelo método 'performDivision'
System.err.println("Falha na divisão: ");
e.printStackTrace(); // Útil para depuração
}
}
// Método que pode lançar uma exceção e a delega ao chamador
public void performDivision(int num, int den) throws ArithmeticException {
if (den == 0) {
// 'throw' é usado para lançar uma exceção explicitamente
throw new ArithmeticException("Tentativa de divisão por zero.");
}
System.out.println("Divisão bem-sucedida: " + (num / den));
}
}
Exceções Personalizadas
Java permite a criação de classes de exceção próprias, estendendo Exception ou suas subclasses. Isso é útil para modelar eros específicos do domínio da aplicação.
Passos para criar e usar exceções personalizadas:
- Definir a classe: Crie uma nova classe que herde de
Exception(ou uma subclasse). - Lançar a exceção: Utilize a palavra-chave
throwpara instanciar e lançar seu objeto de exceção. - Gerenciar a exceção: Se a exceção for tratada no método onde é lançada, use
try-catch. Caso contrário, usethrowsna assinatura do método para delegar o tratamento ao chamador. - Capturar no chamador: O código que chama o método deve estar preparado para capturar e tratar a exceção personalizada.
// Classe de Exceção Personalizada
public class BusinessRuleViolationException extends Exception {
private final String detail;
public BusinessRuleViolationException(String message, String detail) {
super(message); // Chama o construtor da superclasse Exception
this.detail = detail;
}
public String getDetail() {
return detail;
}
@Override
public String toString() {
return "BusinessRuleViolationException [Mensagem: " + getMessage() + ", Detalhe: " + detail + "]";
}
}
public class OrderProcessor {
public static void main(String[] args) {
try {
processOrder(100); // Simula processamento de pedido
} catch (BusinessRuleViolationException brv) {
System.err.println("Erro ao processar pedido: " + brv.toString());
}
}
// Método que pode lançar a exceção personalizada
public void processOrder(double amount) throws BusinessRuleViolationException {
if (amount <= 0) {
throw new BusinessRuleViolationException("Valor do pedido inválido", "O valor deve ser positivo.");
}
System.out.println("Pedido de valor " + amount + " processado com sucesso.");
// Lógica de processamento do pedido...
}
}
/**
* Exemplo de saída (se amount for 0 ou negativo):
* Erro ao processar pedido: BusinessRuleViolationException [Mensagem: Valor do pedido inválido, Detalhe: O valor deve ser positivo.]
*
* Exemplo de saída (se amount for positivo):
* Pedido de valor 100.0 processado com sucesso.
* */
Recomendações Práticas
- Prefira tratar exceções de tempo de execução com lógica de programação robusta e validações.
- Um bloco
catch(Exception e)após capturas específicas pode servir como uma rede de segurança para exceções não previstas, mas evite mascarar erros. - Sempre que possível, utilize
try-catchpara envolver operações que possam falhar. - Evite o uso excessivo de
e.printStackTrace()em produção; prefira logging estruturado. - A estratégia de tratamento deve ser definida com base nos requisitos de negócio e na natureza da exceção.
- Utilize o bloco
finallypara garantir a liberação de recursos (arquivos, conexões de rede, etc.), mesmo que ocorram exceções.