9 Tratamento de Erros
9.1 Por que tratar erros
Em sala, sempre digo: usuário real digita de tudo.
Se você não tratar erro, o programa pode quebrar.
Além de quebrar, ele pode confundir quem está usando.
Um sistema robusto não é aquele que nunca erra, mas aquele que reage bem quando algo sai do esperado.
Tratar erros melhora a experiência do usuário e também facilita a manutenção do código.
9.2 Exemplo com try-catch
No exemplo abaixo, tentamos converter a entrada para inteiro.
Se a conversão funcionar, seguimos o fluxo normal.
Se falhar, capturamos a exceção e mostramos uma mensagem amigável.
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
try {
System.out.print("Digite sua idade: ");
int idade = Integer.parseInt(sc.nextLine());
System.out.println("Idade válida: " + idade);
} catch (NumberFormatException e) {
System.out.println("Bot: valor inválido. Digite um número inteiro.");
}Perceba que o programa não é encerrado abruptamente quando ocorre erro de digitação.
Essa abordagem é essencial em aplicações com interação contínua, como menus e chatbots de console.
9.3 Tipos de erro que você vai encontrar
Nem todo problema em um programa é do mesmo tipo.
Em Java, vale separar mentalmente três grupos:
- Erro de compilação: acontece antes do programa rodar, como variável não declarada ou ponto e vírgula ausente.
- Erro de execução (exceção): aparece com o programa já em funcionamento, por exemplo ao converter texto inválido para número.
- Erro de lógica: o programa roda sem travar, mas entrega resultado incorreto por causa de uma regra mal implementada.
Essa distinção ajuda você a escolher a estratégia certa para correção.
try-catch atua principalmente no segundo grupo: problemas de execução.
9.4 Como o fluxo do try-catch funciona
Quando uma exceção ocorre dentro do bloco try, o Java interrompe imediatamente aquele trecho e procura um catch compatível.
Se encontrar, executa o catch e continua o programa após o bloco.
Se não houver catch compatível, a exceção se propaga e pode encerrar a aplicação.
Por isso, tratar exceções não significa esconder erro, e sim controlar o fluxo para que o sistema reaja com previsibilidade.
9.5 Uso de finally
O bloco finally é executado independentemente de ter ocorrido exceção ou não.
Ele é útil para fechar recursos, liberar conexões e finalizar operações obrigatórias de limpeza.
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
try {
System.out.print("Digite um número inteiro: ");
int valor = Integer.parseInt(sc.nextLine());
System.out.println("Valor lido: " + valor);
} catch (NumberFormatException e) {
System.out.println("Entrada inválida. Use apenas números inteiros.");
} finally {
System.out.println("Fim da tentativa de leitura.");
}Em programas maiores, esquecer limpeza de recurso pode causar vazamentos e comportamentos estranhos ao longo do tempo.
9.6 Mais de um catch
Um mesmo bloco try pode ter vários catch, cada um para um tipo de problema.
Isso permite mensagens mais específicas e ajuda na depuração.
try {
String[] nomes = {"Ana", "Bruno"};
System.out.print("Informe o índice: ");
int indice = Integer.parseInt(new Scanner(System.in).nextLine());
System.out.println("Nome: " + nomes[indice]);
} catch (NumberFormatException e) {
System.out.println("Digite apenas números para o índice.");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Índice fora da faixa válida do vetor.");
}Quando houver vários catch, comece pelos tipos mais específicos e deixe os mais genéricos por último.
9.7 Boas práticas
- Trate erros esperados
- Mostre mensagens claras
- Não esconda erros importantes
- Evite mensagens técnicas para o usuário final
- Registre detalhes do erro para depuração quando necessário
Uma boa prática é separar a mensagem para usuário da mensagem para desenvolvedor.
Assim, você mantém a interface simples sem perder informações importantes para corrigir problemas.
Outro ponto importante é validar entrada antes de processar.
Se o valor precisa estar entre 1 e 10, por exemplo, confirme esse intervalo antes de aplicar a regra de negócio.
Essa validação preventiva reduz exceções evitáveis e melhora a clareza do código.
9.8 Exemplo com repetição até entrada válida
Em aplicações de console, é comum pedir nova tentativa até o usuário digitar corretamente.
Esse padrão é melhor do que encerrar o programa no primeiro erro de entrada.
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
int idade;
while (true) {
try {
System.out.print("Digite sua idade: ");
idade = Integer.parseInt(sc.nextLine());
if (idade < 0) {
System.out.println("A idade não pode ser negativa.");
continue;
}
break;
} catch (NumberFormatException e) {
System.out.println("Valor inválido. Digite um número inteiro.");
}
}
System.out.println("Idade registrada com sucesso: " + idade);Note que o erro é tratado, o usuário é orientado e o fluxo continua controlado.
Esse estilo torna o programa mais resistente para uso real.
9.9 Quando usar throw
Além de capturar exceções, você também pode lançar exceções quando detectar um estado inválido.
Isso é útil para proteger regras de negócio.
public static void validarIdade(int idade) {
if (idade < 0) {
throw new IllegalArgumentException("Idade não pode ser negativa.");
}
}Lançar exceção com mensagem clara facilita encontrar a origem do problema e evita que dados incorretos avancem no sistema.
- Trate erro para opção de menu inválida.
- Permita nova tentativa após erro.
- Teste com entradas não numéricas.
- Crie uma validação para impedir idade negativa.
- Mostre uma mensagem diferente para campo vazio.
Ao terminar, execute os testes com calma e anote os comportamentos observados.
O objetivo não é apenas “fazer funcionar”, mas construir programas confiáveis em cenários reais.
Como prática final, tente explicar em voz alta por que cada catch existe no seu código.
Se você não conseguir justificar um tratamento, provavelmente ele está genérico demais.
Um bom tratamento de erros comunica intenção, protege o fluxo e melhora a experiência de quem usa o programa.
Neste capítulo, o estudo de tratamento de erros se torna realmente valioso quando você deixa de enxergar o conteúdo como uma lista de regras isoladas e passa a observar como cada decisão técnica influencia a qualidade do programa, a facilidade de manutenção e a capacidade de adaptar a solução sem quebrar o que já estava funcionando, especialmente em atividades progressivas que simulam situações de projeto real.
Para consolidar o aprendizado com profundidade, vale estruturar sua prática em uma sequência objetiva na qual você revisa o conceito principal, implementa um exemplo pequeno e legível e, logo em seguida, analisa de maneira crítica se houve melhoria concreta no uso de try-catch, na propagação de exceções e na resiliência, porque esse ciclo consciente transforma estudo passivo em desenvolvimento de critério técnico.
Quando esse processo se repete ao longo das semanas, você começa a perceber que sua evolução não depende de decorar respostas prontas, mas sim de interpretar problemas com mais maturidade, justificar escolhas com argumentos claros e construir soluções cada vez mais consistentes, o que representa exatamente a transição de iniciante para praticante autônomo dentro da trilha de Java.