10  Tratamento de erros e arquivos

Recuperar entradas inválidas e persistir o histórico

10.1 Objetivos de aprendizagem

Ao concluir este capítulo, você deverá ser capaz de:

  • distinguir erro de compilação, exceção e erro de lógica;
  • capturar apenas exceções que podem ser tratadas naquele ponto;
  • repetir a leitura até obter uma entrada válida;
  • lançar IllegalArgumentException para contratos violados;
  • salvar texto em arquivo com tratamento de IOException.

10.2 Exceções esperadas

A entrada é convertida; em caso de sucesso o programa continua e em caso de erro informa o problema e solicita nova entrada.

Recuperação de uma entrada inválida.

Em programas interativos, entradas inválidas são previsíveis. Integer.parseInt lança NumberFormatException quando o texto não representa um inteiro:

try {
    int idade = Integer.parseInt(entrada);
    System.out.println("Idade: " + idade);
} catch (NumberFormatException erro) {
    System.out.println("Digite um número inteiro.");
}

Não capture Exception sem necessidade. Um tipo específico documenta o cenário esperado e evita esconder defeitos diferentes.

10.3 Repetir até obter um valor válido

import java.util.Scanner;

static int lerInteiro(Scanner scanner, String mensagem) {
    while (true) {
        System.out.print(mensagem);
        String entrada = scanner.nextLine();

        try {
            return Integer.parseInt(entrada);
        } catch (NumberFormatException erro) {
            System.out.println("Valor inválido. Tente novamente.");
        }
    }
}

A leitura e a recuperação ficam centralizadas. O restante do programa recebe um inteiro válido.

10.4 Lançar uma exceção

Um estudante propõe capturar Exception para resolver tudo e o JavaBot compara isso a apagar a luz para não ver a bagunça.

Quadrinho: capturar todas as exceções.

Use uma exceção quando o chamador viola o contrato de um método:

static void validarIdade(int idade) {
    if (idade < 0 || idade > 130) {
        throw new IllegalArgumentException("Idade fora do intervalo.");
    }
}

Capturar ou propagar depende da responsabilidade. A interface de console pode capturar e mostrar uma orientação; uma classe de domínio normalmente lança a exceção e deixa a decisão para a camada externa.

10.5 Persistência com try-with-resources

A API Files simplifica a escrita de linhas e gerencia o fechamento do arquivo:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

static void salvarHistorico(Path arquivo, List<String> linhas)
        throws IOException {
    Files.write(arquivo, linhas);
}

No limite externo do programa, trate a falha:

try {
    salvarHistorico(Path.of("historico.txt"), historico);
    System.out.println("Histórico salvo.");
} catch (IOException erro) {
    System.out.println("Não foi possível salvar o histórico.");
    System.err.println("Detalhe técnico: " + erro.getMessage());
}

A mensagem para a pessoa usuária é curta; o detalhe técnico vai para o fluxo de erro.

10.6 Exemplo completo

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

public class Capitulo09ErrosArquivos {
    public static void salvarHistorico(Path arquivo, List<String> linhas)
            throws IOException {
        Files.write(arquivo, linhas);
    }

    public static void main(String[] args) {
        List<String> historico = List.of("USUÁRIO: oi", "BOT: Olá!");

        try {
            salvarHistorico(Path.of("historico.txt"), historico);
            System.out.println("Histórico salvo.");
        } catch (IOException erro) {
            System.out.println("Não foi possível salvar o histórico.");
            System.err.println(erro.getMessage());
        }
    }
}

10.7 Evolução do projeto: versão 9

O chatbot recupera entradas numéricas inválidas e oferece a opção de salvar o histórico ao encerrar. A falha de escrita não destrói a conversa nem mostra uma pilha de exceção para a pessoa usuária.

O exemplo completo está em examples/src/Capitulo09ErrosArquivos.java.

10.8 Prática verificável

  1. Leia uma idade até receber valor numérico entre 0 e 130.
  2. Salve três mensagens em historico.txt.
  3. Teste um caminho de arquivo inválido e confirme a mensagem amigável.
  4. Explique por que NumberFormatException e IOException são tratadas em lugares diferentes.
AvisoNão use exceção como validação comum

Condições simples, como texto vazio, devem ser verificadas diretamente. Exceções são apropriadas quando uma operação não consegue cumprir seu contrato.