1. Tipos de exceções

Todas as exceções são divididas em 4 tipos, que na verdade são classes que herdam umas das outras.

Throwableaula

A classe base para todas as exceções é a Throwableclasse. A Throwableclasse contém o código que grava a pilha de chamada atual (rastreamento de pilha do método atual) em uma matriz. Aprenderemos o que é um rastreamento de pilha um pouco mais tarde.

O operador throw só pode aceitar um objeto que deriva da Throwableclasse. E embora você possa teoricamente escrever códigos como throw new Throwable();, ninguém costuma fazer isso. O principal objetivo da Throwableclasse é ter uma única classe pai para todas as exceções.

Erroraula

A próxima classe de exceção é a Errorclasse, que herda diretamente a Throwableclasse. A máquina Java cria objetos da Errorclasse (e seus descendentes) quando ocorrem problemas sérios . Por exemplo, um mau funcionamento do hardware, memória insuficiente, etc.

Normalmente, como programador, não há nada que você possa fazer em uma situação em que tal erro (do tipo para o qual um Errordeve ser lançado) tenha ocorrido no programa: esses erros são muito sérios. Tudo o que você pode fazer é notificar o usuário de que o programa está travando e/ou gravar todas as informações conhecidas sobre o erro no log do programa.

Exceptionaula

As classes Exceptione RuntimeExceptionsão para erros comuns que acontecem na operação de vários métodos. O objetivo de cada exceção lançada é ser capturado por um catchbloco que saiba como tratá-la adequadamente.

Quando um método não pode concluir seu trabalho por algum motivo, ele deve notificar imediatamente o método de chamada lançando uma exceção do tipo apropriado.

Em outras palavras, se uma variável for igual a null, o método lançará um NullPointerException. Se os argumentos incorretos forem passados ​​para o método, ele lançará um InvalidArgumentException. Se o método acidentalmente dividir por zero, ele lançará um ArithmeticException.

RuntimeExceptionaula

RuntimeExceptionssão um subconjunto de Exceptions. Poderíamos até dizer que RuntimeExceptioné uma versão leve das exceções comuns ( Exception) — menos requisitos e restrições são impostos a tais exceções

Você aprenderá a diferença entre Exceptione RuntimeExceptionmais tarde.


2. Throws: exceções verificadas

Todas as exceções Java se enquadram em 2 categorias: marcadas e desmarcadas .

Todas as exceções que herdam o RuntimeExceptionou Errorsão consideradas exceções não verificadas . Todos os outros são exceções verificadas .

Importante!

Vinte anos depois que as exceções verificadas foram introduzidas, quase todo programador Java pensa nisso como um bug. Em estruturas modernas populares, 95% de todas as exceções não são verificadas. A linguagem C#, que quase copiou o Java exatamente, não adicionou exceções verificadas .

Qual é a principal diferença entre exceções verificadas e não verificadas ?

Existem requisitos adicionais impostos às exceções verificadas . Grosso modo, são estes:

Requisito 1

Se um método lança uma exceção verificada , ele deve indicar o tipo de exceção em sua assinatura . Dessa forma, todo método que o chama está ciente de que essa "exceção significativa" pode ocorrer nele.

Indique as exceções verificadas após os parâmetros do método após a throwspalavra-chave (não use a throwpalavra-chave por engano). Parece algo assim:

type method (parameters) throws exception

Exemplo:

exceção verificada exceção não verificada
public void calculate(int n) throws Exception
{
   if (n == 0)
      throw new Exception("n is null!");
}
public void calculate(n)
{
   if (n == 0)
      throw new RuntimeException("n is null!");
}

No exemplo à direita, nosso código gera uma exceção não verificada — nenhuma ação adicional é necessária. No exemplo à esquerda, o método lança uma exceção verificada , então a throwspalavra-chave é adicionada à assinatura do método junto com o tipo da exceção.

Se um método espera lançar várias exceções verificadas , todas elas devem ser especificadas após a throwspalavra-chave, separadas por vírgulas. A ordem não é importante. Exemplo:

public void calculate(int n) throws Exception, IOException
{
   if (n == 0)
      throw new Exception("n is null!");
   if (n == 1)
      throw new IOException("n is 1");
}

Requisito 2

Se você chamar um método que tenha verificado exceções em sua assinatura, não poderá ignorar o fato de que ele as lança.

Você deve capturar todas essas exceções adicionando catchblocos para cada uma ou adicionando-as a uma throwscláusula para seu método.

É como se estivéssemos dizendo: " Essas exceções são tão importantes que devemos capturá-las. E se não soubermos como tratá-las, qualquer pessoa que chamar nosso método deverá ser notificada de que tais exceções podem ocorrer nele.

Exemplo:

Imagine que estamos escrevendo um método para criar um mundo povoado por humanos. O número inicial de pessoas é passado como um argumento. Portanto, precisamos adicionar exceções se houver poucas pessoas.

Criando Terra Observação
public void createWorld(int n) throws EmptyWorldException, LonelyWorldException
{
   if (n == 0)
      throw new EmptyWorldException("There are no people!");
   if (n == 1)
      throw new LonelyWorldException ("There aren't enough people!");
   System.out.println("A wonderful world was created. Population: " + n);
}
O método potencialmente lança duas exceções verificadas :

  • EmptyWorldException
  • LonelyWorldException

Essa chamada de método pode ser tratada de 3 maneiras:

1. Não pegue nenhuma exceção

Isso é feito com mais frequência quando o método não sabe como lidar adequadamente com a situação.

Código Observação
public void createPopulatedWorld(int population)
throws EmptyWorldException, LonelyWorldException
{
   createWorld(population);
}
O método de chamada não captura as exceções e deve informar os outros sobre elas: ele as adiciona à sua própria throwscláusula

2. Pegue algumas das exceções

Nós lidamos com os erros que podemos lidar. Mas aqueles que não entendemos, jogamos para o método de chamada. Para fazer isso, precisamos adicionar seu nome à cláusula throws:

Código Observação
public void createNonEmptyWorld(int population)
throws EmptyWorldException
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
}
O chamador captura apenas uma exceção verificadaLonelyWorldException — . A outra exceção deve ser adicionada à sua assinatura, indicando-a após a throwspalavra-chave

3. Capture todas as exceções

Se o método não lançar exceções para o método de chamada, o método de chamada sempre terá certeza de que tudo funcionou bem. E será incapaz de tomar qualquer ação para corrigir uma situação excepcional.

Código Observação
public void createAnyWorld(int population)
{
   try
   {
      createWorld(population);
   }
   catch (LonelyWorldException e)
   {
      e.printStackTrace();
   }
   catch (EmptyWorldException e)
   {
      e.printStackTrace();
   }
}
Todas as exceções são capturadas neste método. O chamador terá certeza de que tudo correu bem.


3. Capturando várias exceções

Programadores realmente odeiam duplicar código. Eles até criaram um princípio de desenvolvimento correspondente - DRY : Don't Repeat Yourself. Mas ao lidar com exceções, há ocasiões frequentes em que um trybloco é seguido por vários catchblocos com o mesmo código.

Ou pode haver 3 catchblocos com o mesmo código e outros 2 catchblocos com outro código idêntico. Esta é uma situação padrão quando seu projeto lida com exceções de forma responsável.

A partir da versão 7, a linguagem Java adicionou a capacidade de especificar vários tipos de exceções em um único catchbloco. Parece mais ou menos assim:

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
   // Exception handling code
}

Você pode ter quantos catchblocos quiser. No entanto, um único catchbloco não pode especificar exceções que herdam umas das outras. Em outras palavras, você não pode escrever catch ( Exception| RuntimeExceptione), porque a RuntimeExceptionclasse herda Exception.