CodeGym /Cursos /JAVA 25 SELF /Tratamento avançado de exceções e melhores práticas

Tratamento avançado de exceções e melhores práticas

JAVA 25 SELF
Nível 24 , Lição 3
Disponível

1. Múltiplos catch: tratamento de diferentes exceções

Em programas reais, o mesmo bloco de código pode lançar diferentes tipos de exceções. Por exemplo, ao ler um arquivo pode ocorrer tanto FileNotFoundException quanto IOException e, ao fazer o parsing de dados, também um NumberFormatException. Para lidar corretamente com cada situação, em Java é possível usar vários blocos catch em sequência.

Sintaxe

try {
    // operações arriscadas
} catch (FileNotFoundException e) {
    System.out.println("Arquivo não encontrado: " + e.getMessage());
} catch (IOException e) {
    System.out.println("Erro de E/S: " + e.getMessage());
} catch (NumberFormatException e) {
    System.out.println("Formato de número inválido: " + e.getMessage());
}

Ponto importante:
Os blocos catch devem ir do mais específico para o mais geral! Se você escrever primeiro catch (Exception e), todos os outros blocos se tornarão inalcançáveis e o compilador emitirá um erro.

Por que isso é importante?

  • Tratamento específico: Você pode reagir de maneiras diferentes a erros distintos (por exemplo, sugerir escolher outro arquivo ou repetir a entrada).
  • Legibilidade: O código fica mais claro — é possível ver quais erros são esperados e como são tratados.

2. catch com vários tipos (multi-catch)

Às vezes, diferentes exceções precisam do mesmo tratamento. Por exemplo, você quer apenas exibir uma mensagem de erro ou registrá-lo em log, independentemente de ter sido um erro de leitura de arquivo ou de conversão de número.

Desde o Java 7 surgiu a sintaxe multi-catch, que permite agrupar vários tipos de exceção em um único bloco:

try {
    // operação arriscada
} catch (IOException | NumberFormatException e) {
    System.out.println("Erro ao processar arquivo ou número: " + e.getMessage());
}

Como funciona:

  • Os tipos de exceção são indicados separados por uma barra vertical (|).
  • A variável e terá o tipo da classe base mais geral (normalmente Exception).
  • Dentro do bloco não é permitido atribuir um novo valor à variável e — ela é considerada final automaticamente.

Exemplo:

try {
    BufferedReader reader = new BufferedReader(new FileReader("numbers.txt"));
    String line = reader.readLine();
    int number = Integer.parseInt(line);
    System.out.println("Número: " + number);
    reader.close();
} catch (IOException | NumberFormatException e) {
    System.out.println("Erro: " + e.getMessage());
}

3. Boas práticas de tratamento de exceções

Não engula exceções

Ruim:

try {
    // algo arriscado
} catch (Exception e) {
    // não fazemos nada!
}

Por que isso é ruim?
Um bloco catch vazio “abafa” o erro — você perde informação sobre a causa da falha e o programa se torna impossível de depurar.

Melhor: pelo menos imprima uma mensagem ou registre o erro em log.

catch (Exception e) {
    e.printStackTrace();
}

Se você não puder tratar a exceção — propague-a:

catch (IOException e) {
    throw e; // ou throw new RuntimeException(e);
}

Lance exceções o mais específicas possível

Se você escreve seus métodos ou APIs, sempre lance exceções o mais específicas possível, e não apenas Exception ou RuntimeException. Isso torna seu código e API mais claros para outros desenvolvedores.

Exemplo:

public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException("Saldo insuficiente");
    }
    // ...
}

Não use exceções para controle de fluxo do programa

Exceções destinam-se a tratar situações excepcionais — ou seja, erros que não devem ocorrer no fluxo normal do programa. Não as use para a lógica comum (por exemplo, para sair de um laço ou verificar condições).

Ruim:

try {
    while (true) {
        String line = reader.readLine();
        if (line == null) break;
        // processamento da linha
    }
} catch (Exception e) {
    // usar exceção para sair do laço — ruim!
}

Bom:

String line;
while ((line = reader.readLine()) != null) {
    // processamento da linha
}

4. Exemplos práticos

Exemplo: tratar erros diferentes de maneiras diferentes

try {
    BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
    String line = reader.readLine();
    int number = Integer.parseInt(line);
    System.out.println("Número: " + number);
    reader.close();
} catch (FileNotFoundException e) {
    System.out.println("Arquivo não encontrado!");
} catch (IOException e) {
    System.out.println("Erro de leitura de arquivo!");
} catch (NumberFormatException e) {
    System.out.println("O arquivo não contém um número!");
}

Exemplo: multi-catch para o mesmo tratamento

try {
    // algo arriscado
} catch (IOException | NumberFormatException e) {
    System.out.println("Erro: " + e.getMessage());
}

5. Nuances importantes e erros comuns

Erro nº 1: ordem incorreta dos blocos catch. Primeiro devem vir as exceções mais específicas e depois as gerais. Caso contrário, a execução nunca chegará ao bloco mais amplo.

Erro nº 2: uso de tipos relacionados em multi-catch. Por exemplo, catch (IOException | Exception e) não compila, porque IOException herda de Exception.

Erro nº 3: tentar alterar a variável e em um multi-catch. Nesse caso, e é considerada final — não é permitido atribuir a ela um novo valor.

Erro nº 4: blocos catch vazios. Ignorar erros leva a bugs “silenciosos”. No mínimo, exiba uma mensagem ou registre a exceção em log.

Erro nº 5: capturar todos os Exception de uma vez. Usar catch (Exception e) oculta problemas reais e dificulta a depuração. É melhor capturar apenas as exceções esperadas.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION