1. Tipos de exceções

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

Lança: 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. Exceções de empacotamento

As exceções checadas pareciam legais na teoria, mas acabaram se tornando uma grande frustração na prática.

Suponha que você tenha um método super popular em seu projeto. Ele é chamado de centenas de lugares em seu programa. E você decide adicionar uma nova exceção verificada a ele. E pode ser que essa exceção verificada seja realmente importante e tão especial que apenas o main()método saiba o que fazer se for pego.

Isso significa que você terá que adicionar a exceção verificada à throwscláusula de cada método que chama seu método superpopular . Assim como na throwscláusula de todos os métodos que chamam esses métodos. E dos métodos que chamam esses métodos.

Como resultado, as throwscláusulas de metade dos métodos do projeto recebem uma nova exceção verificada . E é claro que seu projeto é coberto por testes e agora os testes não compilam. E agora você também precisa editar as cláusulas throws em seus testes.

E então todo o seu código (todas as alterações em centenas de arquivos) terá que ser revisado por outros programadores. E neste ponto nos perguntamos por que fizemos tantas mudanças sangrentas no projeto? Dia (s?) de trabalho e testes quebrados - tudo para adicionar uma exceção verificada ?

E, claro, ainda existem problemas relacionados à herança e substituição de métodos. Os problemas que vêm das exceções verificadas são muito maiores do que o benefício. O resultado final é que agora poucas pessoas os amam e poucas pessoas os usam.

No entanto, ainda há muito código (incluindo o código da biblioteca Java padrão) que contém essas exceções verificadas . O que fazer com eles? Não podemos ignorá-los e não sabemos como lidar com eles.

Os programadores Java propuseram agrupar as exceções verificadas em arquivos RuntimeException. Em outras palavras, capture todas as exceções verificadas e, em seguida, crie exceções não verificadasRuntimeException (por exemplo, ) e lance-as. Fazendo isso fica mais ou menos assim:

try
{
   // Code where a checked exception might occur
}
catch(Exception exp)
{
   throw new RuntimeException(exp);
}

Não é uma solução muito bonita, mas não há nada de criminoso aqui: a exceção foi simplesmente colocada dentro de um arquivo RuntimeException.

Se desejar, você pode recuperá-lo facilmente de lá. Exemplo:

Código Observação
try
{
   // Code where we wrap the checked exception
   // in a RuntimeException
}
catch(RuntimeException e)
{
   Throwable cause = e.getCause();
   if (cause instanceof Exception)
   {
      Exception exp = (Exception) cause;
      // Exception handling code goes here
   }
}







Obtenha a exceção armazenada dentro do RuntimeExceptionobjeto. A causevariável pode null

determinar seu tipo e convertê-lo em um tipo de exceção verificada .


4. Capturando várias exceções

Programadores realmente odeiam duplicar código. Eles até criaram um princípio de desenvolvimento correspondente: Don't Repeat Yourself (DRY) . 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 algo 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.



5. Exceções personalizadas

Você sempre pode criar sua própria classe de exceção. Você simplesmente cria uma classe que herda a RuntimeExceptionclasse. Vai parecer algo assim:

class ClassName extends RuntimeException
{
}

Discutiremos os detalhes enquanto você aprende OOP, herança, construtores e substituição de métodos.

No entanto, mesmo se você tiver apenas uma classe simples como esta (totalmente sem código), ainda poderá lançar exceções com base nela:

Código Observação
class Solution
{
   public static void main(String[] args)
   {
      throw new MyException();
   }
}

class MyException extends RuntimeException
{
}




Jogue um desmarcado MyException .

Na missão Java Multithreading , mergulharemos profundamente no trabalho com nossas próprias exceções personalizadas.