Tratamento de Exceções
Tratamento de Exceções em Java - Guia Completo para Iniciantes
O que é uma Exceção?
Imagine que você está programando uma calculadora. De repente, o usuário tenta dividir um número por zero. O que acontece? O programa quebra e para de funcionar? Isso seria péssimo!
Uma exceção é um evento que ocorre durante a execução do programa que interrompe o fluxo normal das instruções. É como um "alerta" que o Java nos dá quando algo inesperado acontece.
public class ExemploExcecao {
public static void main(String[] args) {
int resultado = 10 / 0; // Isso vai gerar uma exceção!
System.out.println("Isso nunca será executado");
}
}
Quando você roda esse código, o Java mostra algo como:
Exception in thread "main" java.lang.ArithmeticException: / by zero
Checked vs Unchecked Exceptions
No Java, as exceções são divididas em dois tipos principais:
Unchecked Exceptions (RuntimeException)
- Não precisam ser tratadas obrigatoriamente pelo programador
- São erros que poderiam ser evitados com uma boa lógica de programação
- Herdam de
RuntimeException
Exemplos comuns:
ArithmeticException- divisão por zeroNullPointerException- acessar método de objeto nuloArrayIndexOutOfBoundsException- acessar índice inválido de arrayNumberFormatException- converter string inválida para número
Checked Exceptions
- Precisam ser tratadas ou declaradas com
throws - São situações que o programador pode prever e deve preparar o código para lidar
- Herdam de
Exception(mas não deRuntimeException)
Exemplos comuns:
IOException- problemas com entrada/saída de dadosSQLException- problemas com banco de dadosFileNotFoundException- arquivo não encontrado
import java.io.*;
public class ExemploChecked {
public static void main(String[] args) {
// Isso NÃO compila sem tratamento!
// FileReader arquivo = new FileReader("arquivo.txt");
// Jeito correto - precisa tratar ou declarar throws
try {
FileReader arquivo = new FileReader("arquivo.txt");
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado!");
}
}
}
Try/Catch - O Bloco de Proteção
O try/catch é como um "seguro" para seu código. Você coloca o código perigoso dentro do try e prepara o que fazer se algo der errado no catch.
Estrutura básica:
public class ExemploBasico {
public static void main(String[] args) {
try {
// Código que pode gerar exceção
int[] numeros = {1, 2, 3};
System.out.println(numeros[5]); // Isso vai dar erro!
} catch (ArrayIndexOutOfBoundsException e) {
// O que fazer se a exceção acontecer
System.out.println("Ops! Você tentou acessar uma posição que não existe!");
}
System.out.println("Programa continua normalmente!");
}
}
Múltiplos Catch - Vários Tipos de Erro
Você pode ter vários catch para tratar diferentes tipos de exceção:
import java.util.Scanner;
public class MultiplosCatch {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try {
System.out.print("Digite um número: ");
String entrada = scanner.nextLine();
int numero = Integer.parseInt(entrada);
System.out.print("Digite outro número: ");
int divisor = Integer.parseInt(scanner.nextLine());
int resultado = numero / divisor;
System.out.println("Resultado: " + resultado);
} catch (NumberFormatException e) {
System.out.println("Erro: Você não digitou um número válido!");
} catch (ArithmeticException e) {
System.out.println("Erro: Não é possível dividir por zero!");
} catch (Exception e) {
System.out.println("Erro inesperado: " + e.getMessage());
}
scanner.close();
}
}
Dica importante: Coloque as exceções mais específicas primeiro e as mais genéricas por último!
Finally - O Bloco que Sempre Executa
O bloco finally é opcional e sempre executa, independente se houve exceção ou não. É perfeito para fechar recursos como arquivos, conexões de banco de dados, etc.
import java.io.*;
public class ExemploFinally {
public static void main(String[] args) {
FileReader arquivo = null;
try {
arquivo = new FileReader("dados.txt");
// Código para ler o arquivo...
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado!");
} finally {
// Isso SEMPRE será executado
System.out.println("Fechando arquivo...");
if (arquivo != null) {
try {
arquivo.close();
} catch (IOException e) {
System.out.println("Erro ao fechar arquivo!");
}
}
}
}
}
Throws - Declarando que seu Método Pode Lançar Exceção
Quando um método pode gerar uma exceção checked, você precisa avisar quem vai usar esse método:
import java.io.*;
public class ExemploThrows {
// Este método avisa que pode lançar uma exceção
public static void lerArquivo(String nome) throws FileNotFoundException, IOException {
FileReader arquivo = new FileReader(nome);
// Código para ler...
arquivo.close();
}
public static void main(String[] args) {
try {
lerArquivo("dados.txt");
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado!");
} catch (IOException e) {
System.out.println("Erro ao ler arquivo!");
}
}
}
Throw - Lançando sua Própria Exceção
Você também pode criar e lançar exceções manualmente com a palavra-chave throw:
public class ExemploThrow {
public static void validarIdade(int idade) {
if (idade < 0) {
// Lançando uma exceção manualmente
throw new IllegalArgumentException("Idade não pode ser negativa!");
}
if (idade > 150) {
throw new IllegalArgumentException("Idade inválida!");
}
System.out.println("Idade válida: " + idade);
}
public static void main(String[] args) {
try {
validarIdade(-5);
} catch (IllegalArgumentException e) {
System.out.println("Erro: " + e.getMessage());
}
validarIdade(25); // Isso funciona normalmente
}
}
Exceções Comuns e Como Evitá-las
NullPointerException
public class EvitarNullPointer {
public static void main(String[] args) {
String texto = null;
// ERRADO - vai gerar NullPointerException
// System.out.println(texto.length());
// CORRETO - sempre verifique se não é null
if (texto != null) {
System.out.println(texto.length());
} else {
System.out.println("A variável está vazia!");
}
}
}
ArrayIndexOutOfBoundsException
public class EvitarArrayIndex {
public static void main(String[] args) {
int[] numeros = {10, 20, 30};
// ERRADO
// System.out.println(numeros[3]);
// CORRETO - verifique o tamanho do array
int indice = 3;
if (indice >= 0 && indice < numeros.length) {
System.out.println(numeros[indice]);
} else {
System.out.println("Índice inválido!");
}
}
}
NumberFormatException
public class EvitarNumberFormat {
public static void main(String[] args) {
String texto = "abc";
// ERRADO
// int numero = Integer.parseInt(texto);
// CORRETO - use try/catch
try {
int numero = Integer.parseInt(texto);
System.out.println("Número: " + numero);
} catch (NumberFormatException e) {
System.out.println("Não foi possível converter para número!");
}
}
}
ClassCastException
public class EvitarClassCast {
public static void main(String[] args) {
Object objeto = "Texto";
// ERRADO - vai gerar ClassCastException
// Integer numero = (Integer) objeto;
// CORRETO - use instanceof para verificar
if (objeto instanceof Integer) {
Integer numero = (Integer) objeto;
System.out.println("Número: " + numero);
} else {
System.out.println("O objeto não é um Integer!");
}
}
}
Criando Exceções Customizadas
Você pode criar suas próprias exceções para situações específicas do seu programa:
// Criando nossa própria exceção
class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException(String mensagem) {
super(mensagem);
}
}
class ContaBancaria {
private double saldo;
public ContaBancaria(double saldoInicial) {
this.saldo = saldoInicial;
}
public void sacar(double valor) throws SaldoInsuficienteException {
if (valor > saldo) {
throw new SaldoInsuficienteException(
"Saldo insuficiente! Você tem R$ " + saldo +
", mas tentou sacar R$ " + valor
);
}
saldo -= valor;
System.out.println("Saque de R$ " + valor + " realizado com sucesso!");
}
public double getSaldo() {
return saldo;
}
}
public class ExemploCustomizada {
public static void main(String[] args) {
ContaBancaria conta = new ContaBancaria(1000.00);
try {
conta.sacar(500.00); // OK
conta.sacar(600.00); // Vai lançar exceção!
} catch (SaldoInsuficienteException e) {
System.out.println("Erro: " + e.getMessage());
}
System.out.println("Saldo atual: R$ " + conta.getSaldo());
}
}
Exemplo Prático: Calculadora Robusta
Vamos criar uma calculadora que trata todos os possíveis erros:
import java.util.Scanner;
public class CalculadoraRobusta {
public static double dividir(double a, double b) {
if (b == 0) {
throw new ArithmeticException("Divisão por zero não é permitida!");
}
return a / b;
}
public static double raizQuadrada(double numero) {
if (numero < 0) {
throw new IllegalArgumentException(
"Não é possível calcular raiz quadrada de número negativo!"
);
}
return Math.sqrt(numero);
}
public static int lerInteiro(Scanner scanner, String mensagem) {
while (true) {
try {
System.out.print(mensagem);
String entrada = scanner.nextLine();
return Integer.parseInt(entrada);
} catch (NumberFormatException e) {
System.out.println("Erro: Digite um número inteiro válido!");
}
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("=== CALCULADORA ROBUSTA ===");
System.out.println("1 - Divisão");
System.out.println("2 - Raiz Quadrada");
System.out.print("Escolha uma opção: ");
try {
int opcao = Integer.parseInt(scanner.nextLine());
switch (opcao) {
case 1:
int a = lerInteiro(scanner, "Digite o primeiro número: ");
int b = lerInteiro(scanner, "Digite o segundo número: ");
try {
double resultado = dividir(a, b);
System.out.println(a + " / " + b + " = " + resultado);
} catch (ArithmeticException e) {
System.out.println("Erro: " + e.getMessage());
}
break;
case 2:
int numero = lerInteiro(scanner, "Digite um número: ");
try {
double resultado = raizQuadrada(numero);
System.out.println("√" + numero + " = " + resultado);
} catch (IllegalArgumentException e) {
System.out.println("Erro: " + e.getMessage());
}
break;
default:
System.out.println("Opção inválida!");
}
} catch (NumberFormatException e) {
System.out.println("Erro: Opção inválida!");
} finally {
scanner.close();
System.out.println("Calculadora encerrada.");
}
}
}
Erros Comuns que Iniciantes Cometem
1. Catch Genérico Demais
// ERRADO - muito genérico
try {
// código...
} catch (Exception e) {
// Trata tudo igual
}
// CORRETO - específico
try {
// código...
} catch (NumberFormatException e) {
System.out.println("Erro de formato!");
} catch (ArithmeticException e) {
System.out.println("Erro matemático!");
}
2. Silenciar Exceções
// ERRADO - silenciando a exceção
try {
int numero = Integer.parseInt("abc");
} catch (Exception e) {
// Não faz nada! Isso é péssimo!
}
// CORRETO - trate adequadamente
try {
int numero = Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println("Erro ao converter: " + e.getMessage());
e.printStackTrace(); // Mostra onde ocorreu o erro
}
3. Não Fechar Recursos
// ERRADO - não fecha o scanner
Scanner scanner = new Scanner(System.in);
try {
// código...
} catch (Exception e) {
// tratamento
}
// Scanner nunca é fechado!
// CORRETO - use finally ou try-with-resources
try (Scanner scanner = new Scanner(System.in)) {
// código...
} catch (Exception e) {
// tratamento
}
// Scanner é fechado automaticamente!
4. Tratar Exceção Muito Cedo
// ERRADO - trata onde não deveria
public void processar() {
try {
salvarDados();
} catch (Exception e) {
System.out.println("Erro ao salvar");
}
}
// CORRETO - deixa quem chama decidir
public void processar() throws Exception {
salvarDados(); // Quem chama é que trata
}
Dicas Finais
- Sempre trate exceções - programas que quebram são frustrantes para o usuário
- Seja específico - trate exceções específicas, não use
catch (Exception e)sem necessidade - Mensagens claras - mostre mensagens que o usuário entenda
- Feche recursos - use
finallyoutry-with-resourcespara garantir que recursos sejam liberados - Documente - se seu método pode lançar exceções, documente isso com JavaDoc
- Não exagere - nem todo erro precisa ser uma exceção, às vezes um if/else resolve
Lembre-se: o tratamento de exceções não é sobre evitar erros, mas sobre lidar com eles de forma elegante e informativa para o usuário!