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.
Throwable
aula
A classe base para todas as exceções é a Throwable
classe. A Throwable
classe 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 Throwable
classe. E embora você possa teoricamente escrever códigos como throw new Throwable();
, ninguém costuma fazer isso. O principal objetivo da Throwable
classe é ter uma única classe pai para todas as exceções.
Error
aula
A próxima classe de exceção é a Error
classe, que herda diretamente a Throwable
classe. A máquina Java cria objetos da Error
classe (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 Error
deve 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.
Exception
aula
As classes Exception
e RuntimeException
sã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 catch
bloco 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
.
RuntimeException
aula
RuntimeExceptions
sã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 Exception
e RuntimeException
mais 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 RuntimeException
ou Error
são consideradas exceções não verificadas . Todos os outros são exceções verificadas .
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 throws
palavra-chave (não use a throw
palavra-chave por engano). Parece algo assim:
type method (parameters) throws exception
Exemplo:
exceção verificada | exceção não verificada |
---|---|
|
|
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 throws
palavra-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 throws
palavra-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 catch
blocos para cada uma ou adicionando-as a uma throws
clá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 |
---|---|
|
O método potencialmente lança duas exceções verificadas :
|
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 |
---|---|
|
O método de chamada não captura as exceções e deve informar os outros sobre elas: ele as adiciona à sua própria throws clá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 |
---|---|
|
O chamador captura apenas uma exceção verificadaLonelyWorldException — . A outra exceção deve ser adicionada à sua assinatura, indicando-a após a throws palavra-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 |
---|---|
|
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 à throws
cláusula de cada método que chama seu método superpopular . Assim como na throws
cláusula de todos os métodos que chamam esses métodos. E dos métodos que chamam esses métodos.
Como resultado, as throws
clá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 |
---|---|
|
Obtenha a exceção armazenada dentro do RuntimeException objeto. A cause variá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 try
bloco é seguido por vários catch
blocos com o mesmo código.
Ou pode haver 3 catch
blocos com o mesmo código e outros 2 catch
blocos 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 catch
bloco. Parece algo assim:
try
{
// Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
// Exception handling code
}
Você pode ter quantos catch
blocos quiser. No entanto, um único catch
bloco não pode especificar exceções que herdam umas das outras. Em outras palavras, você não pode escrever catch ( Exception
| RuntimeException
e), porque a RuntimeException
classe 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 RuntimeException
classe. 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 |
---|---|
|
Jogue um desmarcado MyException . |
Na missão Java Multithreading , mergulharemos profundamente no trabalho com nossas próprias exceções personalizadas.
GO TO FULL VERSION