W3docs

Java try...catch

Trate erros em tempo de execução no Java com blocos try...catch para manter seu programa funcionando quando algo dá errado.

try/catch é a menor unidade de tratamento de exceções em Java. Você envolve o código arriscado em try e o segue com um ou mais blocos catch que dizem o que fazer se um determinado tipo de exceção for lançada. Juntos, eles formam uma única instrução; não é possível ter um sem o outro (ou sem um finally, que veremos em um capítulo posterior).

Anatomia

try {
  // code that might throw
} catch (ExceptionType e) {
  // code that runs only if a matching exception was thrown
}

Alguns detalhes passam despercebidos à primeira vista:

  • A variável no catch (e por convenção) tem escopo limitado ao bloco catch. Você pode nomeá-la como quiser; e e ex são as escolhas mais comuns.
  • O parâmetro do catch é efetivamente final — você pode lê-lo, mas não deve reatribuí-lo. Tratá-lo como imutável torna o código mais fácil de acompanhar.
  • O bloco try tem seu próprio escopo. Variáveis declaradas dentro dele não são visíveis para o catch nem após a instrução. Se você precisar de um valor calculado dentro do try mais tarde, declare-o fora.
String content;
try {
  content = Files.readString(path);
} catch (IOException e) {
  content = "";   // works because `content` was declared outside the try
}
System.out.println(content);

Quais catches correspondem

Um bloco catch (T e) é executado quando a exceção lançada é uma instância de T — incluindo qualquer subtipo de T. Portanto:

  • catch (Exception e) captura quase tudo: IOException, NullPointerException, suas próprias exceções personalizadas.
  • catch (RuntimeException e) captura erros de tempo de execução, mas não exceções verificadas como IOException.
  • catch (NullPointerException e) captura apenas NPEs (e subclasses, das quais tipicamente não há nenhuma).

Dentro do try, apenas o primeiro catch correspondente é executado. Se você listar um tipo mais abrangente antes de um mais específico, o catch mais específico ficará inacessível e o compilador recusará:

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

Sempre liste os catches do mais específico ao mais geral.

O que fazer em um catch

Um bloco catch não é um lugar para fazer as exceções desaparecerem. É um lugar para decidir o que fazer diante de uma falha conhecida. As opções realistas são:

  • Recuperar — repetir a tentativa, usar um valor padrão, mudar para um recurso diferente. Este é o melhor caso, e é mais raro do que se pensa.
  • Registrar e relançar — gravar detalhes e deixar a exceção continuar se propagando para um tratador de nível superior que saiba o que fazer.
  • Encapsular e relançar — traduzir uma exceção de baixo nível para uma que se encaixe no vocabulário desta camada (IOExceptionConfigLoadException).
  • Traduzir para um valor de retorno — quando a falha é genuinamente esperada (por exemplo, ao analisar a entrada do usuário), retorne Optional.empty() ou um valor sentinela.

O que não se deve fazer é capturar uma exceção e continuar silenciosamente sem registrá-la ou agir sobre ela. É assim que os bugs desaparecem no ar. Voltaremos a isso no capítulo de boas práticas.

Lendo informações de exceção

O parâmetro do catch é um objeto. Três métodos que você usará constantemente:

  • e.getMessage() — a descrição legível por humanos. Pode ser null.
  • e.toString() — nome da classe mais a mensagem, por exemplo java.io.IOException: file not found.
  • e.printStackTrace() — escreve o rastreamento em System.err. Conveniente durante a depuração, mas use um logger real no código de produção (ele encaminha o rastreamento pelo mesmo canal que todo o resto).

Um quarto método, e.getCause(), retorna a exceção subjacente quando uma foi encapsulada — útil quando você precisa entender a falha original dentro de uma camada de tradução.

Capturar Exception vs. capturar Throwable

Você pode escrever catch (Throwable t) e isso capturará tudo — incluindo Errors como OutOfMemoryError. Não faça isso. Errors significam que a JVM está em apuros e o seu código não está em condições de reagir a eles de forma sensata. Capturar Throwable mascara bugs que deveriam travar o processo.

Atenha-se a Exception ou a um de seus subtipos. Se você realmente precisar registrar o inesperado antes de deixá-lo morrer, o formato correto é catch (RuntimeException e) { log; throw; }.

Um exemplo prático

Um pequeno utilitário que analisa um inteiro de cada linha de entrada. Algumas linhas são números válidos, outras não, e uma é null. Usamos try/catch para continuar passando pelas linhas inválidas e contabilizar o que aconteceu. Chamar line.trim() lança uma NullPointerException na entrada null, enquanto Integer.parseInt lança uma NumberFormatException em qualquer coisa que não seja um inteiro — dois tipos distintos de exceção, cada um com seu próprio catch.

java— editable, runs on the server

A saída é:

not an integer: "hello"
null line — skipped
not an integer: "3.14"
---
parsed: 3, failed: 3, sum: 118

Dois pontos a observar. Primeiro, o programa termina. Sem os catches, a primeira linha inválida encerraria a execução e nada depois dela seria contabilizado. Segundo, as duas falhas são distinguidas: a linha null segue o ramo NullPointerException e exibe sua própria mensagem, enquanto "hello" e "3.14" seguem o ramo NumberFormatException. Selecionar catches por tipo é como você evita que falhas diferentes se misturem.

O que vem a seguir

Um único catch é o caso mais simples. O código real geralmente precisa lidar com várias falhas diferentes ao mesmo tempo. Continue para Múltiplos blocos catch e multi-catch em Java. Depois disso, o bloco finally e o try-with-resources completam a sintaxe, enquanto throw e throws cobrem o outro lado: sinalizar falhas a partir do seu próprio código.

Prática

Prática
Um bloco try executa `a[5] = 1;` em um array de 2 elementos, com `catch (RuntimeException e)` escrito primeiro e `catch (ArrayIndexOutOfBoundsException e)` escrito segundo. O que acontece?
Um bloco try executa `a[5] = 1;` em um array de 2 elementos, com `catch (RuntimeException e)` escrito primeiro e `catch (ArrayIndexOutOfBoundsException e)` escrito segundo. O que acontece?
Was this page helpful?