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.
GO TO FULL VERSION