W3docs

Java throw e throws

Lance exceções manualmente com throw e declare exceções em assinaturas de métodos com throws no Java.

Até agora estávamos capturando exceções lançadas por outro código. Agora você verá como lançar as suas próprias. Duas palavras-chave fazem a maior parte do trabalho — e é fácil confundi-las porque diferem em apenas uma letra.

  • throw — um comando que lança uma exceção em tempo de execução. Uma palavra, em código que roda.
  • throws — uma declaração na assinatura de um método que diz "este método pode lançar estes tipos de exceção." É verificado pelo compilador, nunca é executado.

throw acontece. throws avisa. Mantenha esse par em mente.

Lançando uma exceção

throw recebe uma expressão do tipo Throwable (ou qualquer subtipo) e a lança. O método atual sai imediatamente, a pilha começa a ser desempilhada e a exceção inicia sua busca por um catch correspondente.

if (amount < 0) {
  throw new IllegalArgumentException("amount must be non-negative, got " + amount);
}

Três detalhes:

  • Você só pode lançar um Throwable. O compilador garante isso — throw "oops"; não compila.
  • Você sempre lança uma instância, não uma classe. throw new X(...), nunca throw X.
  • A instância pode ser uma que você criou inline (comum), ou um objeto preexistente (raro — exceções carregam rastreamentos de pilha a partir da sua construção, então reutilizar uma congela o rastreamento errado).

Quando lançar

Lance uma exceção quando o método atual não consegue cumprir seu contrato. Alguns casos claros:

  • Argumentos inválidosIllegalArgumentException para "você me chamou errado."
  • Estado incorretoIllegalStateException para "você me chamou no momento errado" (por exemplo, next() em um iterador vazio).
  • Dados ausentes — exceções específicas do domínio como UserNotFoundException.
  • Operações externas com falha — erros de IO, erros de rede. Geralmente esses vêm da chamada que você acabou de fazer, então você não os constrói; você os deixa propagar, ou os envolve em uma exceção de nível superior.

O caso em que não se deve lançar: como atalho de fluxo de controle para resultados normais. "Lançar para controle de fluxo" é lento e confuso. Se "não encontrado" é um resultado comum, retorne um Optional<T>, não um NotFoundException.

Escolhendo um tipo

As exceções integradas de java.lang cobrem a maioria dos casos sem cerimônia:

  • IllegalArgumentException — argumento ruim
  • IllegalStateException — estado incorreto
  • NullPointerException — um argumento obrigatório era null (use Objects.requireNonNull)
  • UnsupportedOperationException — operação não implementada (por exemplo, add em uma lista imutável)
  • ArithmeticException — falha matemática

Quando a falha é específica do seu domínio — "usuário não encontrado," "cupom inválido," "configuração fora de sincronia" — escreva uma classe personalizada para ela. Dois capítulos adiante faremos exatamente isso.

A cláusula throws

Se o seu método pode lançar uma exceção verificada que ele mesmo não captura, você deve declará-la:

public Config loadConfig(Path p) throws IOException, ParseException {
  String text = Files.readString(p);
  return parser.parse(text);
}

A cláusula faz parte do contrato do método. Ela diz a cada chamador: "se você me chamar, você precisa ou capturar essas exceções ou declará-las você mesmo." O compilador garante isso — é isso que as torna verificadas.

Algumas regras:

  • Você declara apenas exceções verificadas. RuntimeExceptions e suas subclasses são não verificadas — declará-las é permitido mas não obrigatório, e geralmente não é feito.
  • Você pode declarar mais tipos do que realmente lança — útil quando você está mantendo a opção aberta para implementações futuras, embora seja um pequeno ruído.
  • Um método que sobrescreve outro pode declarar as mesmas ou menos exceções verificadas do que o pai (e apenas subtipos das declaradas). Não pode adicionar novas. Isso é a substituição de Liskov aplicada a exceções.

throw e throws juntos

Um método real geralmente faz os dois:

public User loadUser(String id) throws IOException {
  if (id == null || id.isBlank()) {
    throw new IllegalArgumentException("id must be non-blank");
  }
  String json = httpClient.get("/users/" + id);   // may throw IOException
  return parser.toUser(json);
}
  • O throws IOException declara a exceção verificada que pode vir de httpClient.get.
  • O throw new IllegalArgumentException(...) lança uma não verificada para entrada inválida. Ela não precisa aparecer na cláusula throws.

Encapsulando uma exceção

Quando uma exceção de baixo nível não é significativa na sua camada, encapsule-a em uma que seja. Passe a original como causa para que o rastreamento permaneça intacto:

try {
  return Files.readString(configPath);
} catch (IOException e) {
  throw new ConfigLoadException("could not load config from " + configPath, e);
}

O padrão do construtor de segundo argumento — (message, cause) — é padrão em Exception, IOException e todos os integrados. Quando você escreve sua própria classe de exceção, forneça ambos os construtores.

Um exemplo prático

Um pequeno auxiliar no estilo bancário que valida a entrada com IllegalArgumentException, sinaliza uma conta vazia com IllegalStateException, e deixa uma exceção verificada subir para o chamador via throws. O driver mostra como cada uma fica quando lançada.

java— editable, runs on the server

Os três casos em tempo de execução — argumento, estado e saque bem-sucedido — passam por um único catch. O quarto, archive(), só compila porque main pode capturar Exception e porque archive() declarou throws ArchiveException. Tente remover a cláusula throws e o programa falha ao compilar.

O que vem a seguir

O compilador trata algumas exceções de forma estrita (você deve tratá-las) e outras de forma permissiva (você não precisa). Essa divisão é o próximo capítulo. Continue em Exceções verificadas vs. não verificadas no Java.

Prática

Prática
A assinatura de um método é `public void save() throws IOException`. O corpo do método está vazio — ele não lança nada. Vai compilar?
A assinatura de um método é `public void save() throws IOException`. O corpo do método está vazio — ele não lança nada. Vai compilar?
Was this page helpful?