W3docs

Múltiplos blocos catch e multi-catch em Java

Capture diferentes tipos de exceção em Java com múltiplos blocos catch ou uma cláusula multi-catch (Tipo1 | Tipo2 e).

Um único try pode ser seguido por qualquer número de blocos catch. Cada um declara um tipo de exceção diferente e é executado somente se o lançamento corresponder. É assim que o Java permite que um bloco de código reaja de forma diferente dependendo do que deu errado — uma falha de rede não é um erro de análise, e você pode querer tratá-los de maneiras completamente diferentes.

Múltiplos catches sequenciais

try {
  String body = httpClient.get(url);
  Config cfg  = parser.parse(body);
  apply(cfg);
} catch (IOException e) {
  retryLater(url);
} catch (ParseException e) {
  report("config file is malformed: " + e.getMessage());
} catch (SecurityException e) {
  report("not allowed to apply config");
}

Apenas um catch é executado por lançamento — o primeiro cujo tipo é uma correspondência instanceof para a exceção lançada. Após sua execução, o controle pula para a instrução após toda a instrução try, não para o próximo catch.

A ordem importa: do específico ao geral

Um catch (T e) corresponde a qualquer coisa que seja T ou qualquer subclasse de T. Se você listar uma superclasse antes de sua subclasse, o catch da subclasse fica inacessível e o compilador recusa:

try { ... }
catch (Exception e)        { ... }   // matches everything below Exception
catch (IOException e)      { ... }   // ERROR: unreachable

A ordem a seguir é tipo mais específico no topo, mais amplo na base:

try { ... }
catch (FileNotFoundException e) { ... }   // most specific
catch (IOException e)           { ... }   // wider
catch (Exception e)             { ... }   // catch-all (used sparingly)

Isso também é um exercício útil de design: escrever os catches força você a pensar sobre quais falhas o bloco pode realmente produzir.

Multi-catch (um bloco, vários tipos)

O Java 7 adicionou a forma multi-catch: um único bloco que trata vários tipos de exceção não relacionados, separados por |:

try {
  return parser.read(file);
} catch (IOException | ParseException e) {
  log.warn("could not load config: " + e);
  return Config.defaults();
}

Use isso quando o tratamento for idêntico para várias falhas distintas. É mais curto do que dois blocos catch quase duplicados e torna óbvia a relação "estes compartilham uma resposta" à primeira vista.

Regras para saber:

  • Os tipos na união não devem ser relacionados por herança. IOException | FileNotFoundException não compila — um é subtipo do outro, então o mais amplo já o cobre.
  • Dentro do bloco, e é tipado como o supertipo comum dos tipos listados. Você pode chamar métodos declarados nesse supertipo, mas não os específicos do subtipo. Para a maioria dos usos (log, encapsulamento), getMessage() e toString() são suficientes.
  • O parâmetro catch em um multi-catch é implicitamente final — você não pode reatribuí-lo. (Catches de tipo único são apenas efetivamente finais; a diferença não importa na prática.)

Quando dividir um try

Um erro comum de legibilidade é envolver um método inteiro em um único try gigante e depois capturar tudo que qualquer linha pode lançar. A lógica de tratamento no final fica confusa sobre qual linha falhou.

Duas formas mais limpas quando isso acontece:

  • Duas instruções try separadas, cada uma com escopo para um conjunto relacionado de operações.
  • Um try dentro de um método que chama métodos menores, cada um responsável por um tipo de falha.

Quanto menor o try, mais fácil é raciocinar sobre os catches. "Qual linha poderia lançar isto?" deve sempre ter uma resposta curta.

Um exemplo trabalhado

Um pequeno programa que faz três coisas — analisar um número, pesquisá-lo em um array, dividir por ele — cada uma das quais pode falhar à sua própria maneira. Capturamos cada falha com um handler dedicado para que as mensagens permaneçam específicas. O último catch usa a forma multi-catch para agrupar duas falhas que compartilham uma resposta.

java— editable, runs on the server

Cada entrada percorre um caminho diferente pelos catches, mas cada iteração imprime uma linha limpa — o programa nunca decepciona o usuário.

O que vem a seguir

try/catch trata o caminho feliz e o caminho de falha. A terceira cláusula, finally, trata as coisas que precisam acontecer de qualquer forma. Continue para bloco finally em Java.

Prática

Prática
Este código vai compilar?\n\n```java\ntry { ... }\ncatch (IOException | FileNotFoundException e) {\n log(e);\n}\n```
Este código vai compilar?\n\n```java\ntry { ... }\ncatch (IOException | FileNotFoundException e) {\n log(e);\n}\n```
Was this page helpful?