Java - exceções

Uma exceção (ou evento excepcional) é um problema que surge durante a execução de um programa. Quando umException ocorrer, o fluxo normal do programa é interrompido e o programa / Aplicativo é encerrado de forma anormal, o que não é recomendado, portanto, essas exceções devem ser tratadas.

Uma exceção pode ocorrer por diversos motivos. A seguir estão alguns cenários em que ocorre uma exceção.

  • Um usuário inseriu dados inválidos.

  • Um arquivo que precisa ser aberto não foi encontrado.

  • Uma conexão de rede foi perdida no meio das comunicações ou o JVM ficou sem memória.

Algumas dessas exceções são causadas por erro do usuário, outras por erro do programador e outras por recursos físicos que falharam de alguma maneira.

Com base nisso, temos três categorias de exceções. Você precisa entendê-los para saber como funciona o tratamento de exceções em Java.

  • Checked exceptions- Uma exceção verificada é uma exceção que é verificada (notificada) pelo compilador em tempo de compilação; elas também são chamadas de exceções de tempo de compilação. Essas exceções não podem ser simplesmente ignoradas, o programador deve cuidar (manipular) essas exceções.

Por exemplo, se você usar FileReaderclasse em seu programa para ler dados de um arquivo, se o arquivo especificado em seu construtor não existir, ocorre uma FileNotFoundException e o compilador solicita que o programador trate a exceção.

Exemplo

import java.io.File;
import java.io.FileReader;

public class FilenotFound_Demo {

   public static void main(String args[]) {		
      File file = new File("E://file.txt");
      FileReader fr = new FileReader(file); 
   }
}

Se você tentar compilar o programa acima, obterá as seguintes exceções.

Resultado

C:\>javac FilenotFound_Demo.java
FilenotFound_Demo.java:8: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
      FileReader fr = new FileReader(file);
                      ^
1 error

Note - Desde os métodos read() e close() da classe FileReader lança IOException, você pode observar que o compilador notifica para manipular IOException, junto com FileNotFoundException.

  • Unchecked exceptions- Uma exceção não verificada é uma exceção que ocorre no momento da execução. Estes também são chamados deRuntime Exceptions. Isso inclui bugs de programação, como erros de lógica ou uso impróprio de uma API. As exceções de tempo de execução são ignoradas no momento da compilação.

Por exemplo, se você declarou uma matriz de tamanho 5 em seu programa e tenta chamar o elemento da matriz, ocorre uma exceção ArrayIndexOutOfBoundsException .

Exemplo

public class Unchecked_Demo {
   
   public static void main(String args[]) {
      int num[] = {1, 2, 3, 4};
      System.out.println(num[5]);
   }
}

Se você compilar e executar o programa acima, obterá a seguinte exceção.

Resultado

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
  • Errors- Não são exceções de forma alguma, mas problemas que surgem além do controle do usuário ou do programador. Os erros são normalmente ignorados em seu código porque você raramente pode fazer algo sobre um erro. Por exemplo, se ocorrer um estouro de pilha, ocorrerá um erro. Eles também são ignorados no momento da compilação.

Hierarquia de exceção

Todas as classes de exceção são subtipos da classe java.lang.Exception. A classe de exceção é uma subclasse da classe Throwable. Além da classe de exceção, há outra subclasse chamada Error, que é derivada da classe Throwable.

Os erros são condições anormais que acontecem em caso de falhas graves e não são tratadas pelos programas Java. Erros são gerados para indicar erros gerados pelo ambiente de tempo de execução. Exemplo: JVM está sem memória. Normalmente, os programas não podem se recuperar de erros.

A classe Exception possui duas subclasses principais: IOException class e RuntimeException Class.

A seguir está uma lista das exceções integradas de Java mais comuns verificadas e não verificadas .

Métodos de exceção

A seguir está a lista de métodos importantes disponíveis na classe Throwable.

Sr. Não. Método e Descrição
1

public String getMessage()

Retorna uma mensagem detalhada sobre a exceção que ocorreu. Esta mensagem é inicializada no construtor Throwable.

2

public Throwable getCause()

Retorna a causa da exceção conforme representado por um objeto Throwable.

3

public String toString()

Retorna o nome da classe concatenada com o resultado de getMessage ().

4

public void printStackTrace()

Imprime o resultado de toString () junto com o rastreamento de pilha em System.err, o fluxo de saída de erro.

5

public StackTraceElement [] getStackTrace()

Retorna uma matriz contendo cada elemento no rastreamento da pilha. O elemento no índice 0 representa o topo da pilha de chamadas e o último elemento da matriz representa o método na parte inferior da pilha.

6

public Throwable fillInStackTrace()

Preenche o rastreamento da pilha deste objeto Throwable com o rastreamento da pilha atual, adicionando a qualquer informação anterior no rastreamento da pilha.

Captura de exceções

Um método captura uma exceção usando uma combinação de try e catchpalavras-chave. Um bloco try / catch é colocado ao redor do código que pode gerar uma exceção. O código dentro de um bloco try / catch é referido como código protegido, e a sintaxe para usar try / catch se parece com a seguinte -

Sintaxe

try {
   // Protected code
} catch (ExceptionName e1) {
   // Catch block
}

O código que está sujeito a exceções é colocado no bloco try. Quando ocorre uma exceção, essa exceção ocorrida é tratada pelo bloco catch associado a ela. Cada bloco try deve ser seguido imediatamente por um bloco catch ou bloco finally.

Uma instrução catch envolve a declaração do tipo de exceção que você está tentando capturar. Se ocorrer uma exceção no código protegido, o bloco catch (ou blocos) que segue o try é verificado. Se o tipo de exceção que ocorreu estiver listado em um bloco catch, a exceção é passada para o bloco catch da mesma forma que um argumento é passado para um parâmetro de método.

Exemplo

A seguir está uma matriz declarada com 2 elementos. Em seguida, o código tenta acessar o terceiro elemento da matriz, o que gera uma exceção.

// File Name : ExcepTest.java
import java.io.*;

public class ExcepTest {

   public static void main(String args[]) {
      try {
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

Isso produzirá o seguinte resultado -

Resultado

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

Vários blocos de captura

Um bloco try pode ser seguido por vários blocos catch. A sintaxe para vários blocos catch se parece com a seguinte -

Sintaxe

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}

As instruções anteriores demonstram três blocos catch, mas você pode ter qualquer número deles após uma única tentativa. Se ocorrer uma exceção no código protegido, a exceção é lançada para o primeiro bloco catch na lista. Se o tipo de dados da exceção lançada corresponder a ExceptionType1, ele será detectado lá. Caso contrário, a exceção passa para a segunda instrução catch. Isso continua até que a exceção seja capturada ou falhe em todas as capturas, caso em que o método atual interrompe a execução e a exceção é lançada para o método anterior na pilha de chamadas.

Exemplo

Aqui está o segmento de código que mostra como usar várias instruções try / catch.

try {
   file = new FileInputStream(fileName);
   x = (byte) file.read();
} catch (IOException i) {
   i.printStackTrace();
   return -1;
} catch (FileNotFoundException f) // Not valid! {
   f.printStackTrace();
   return -1;
}

Captura de vários tipos de exceções

Desde o Java 7, você pode manipular mais de uma exceção usando um único bloco catch, esse recurso simplifica o código. Aqui está como você faria isso -

catch (IOException|FileNotFoundException ex) {
   logger.log(ex);
   throw ex;

As Palavras-chave Throws / Throw

Se um método não lida com uma exceção verificada, o método deve declará-lo usando o throwspalavra-chave. A palavra-chave throws aparece no final da assinatura de um método.

Você pode lançar uma exceção, uma recém-instanciada ou uma exceção que você acabou de capturar, usando o throw palavra-chave.

Tente entender a diferença entre throws e throw keywords, throws é usado para adiar o tratamento de uma exceção verificada e throw é usado para invocar uma exceção explicitamente.

O método a seguir declara que lança uma RemoteException -

Exemplo

import java.io.*;
public class className {

   public void deposit(double amount) throws RemoteException {
      // Method implementation
      throw new RemoteException();
   }
   // Remainder of class definition
}

Um método pode declarar que lança mais de uma exceção; nesse caso, as exceções são declaradas em uma lista separada por vírgulas. Por exemplo, o método a seguir declara que lança uma RemoteException e uma InsufficientFundsException -

Exemplo

import java.io.*;
public class className {

   public void withdraw(double amount) throws RemoteException, 
      InsufficientFundsException {
      // Method implementation
   }
   // Remainder of class definition
}

O bloco finalmente

O bloco finalmente segue um bloco try ou um bloco catch. Um bloco final de código sempre é executado, independentemente da ocorrência de uma Exceção.

O uso de um bloco finally permite que você execute quaisquer instruções do tipo de limpeza que você deseja executar, não importa o que aconteça no código protegido.

Um bloco finalmente aparece no final dos blocos catch e tem a seguinte sintaxe -

Sintaxe

try {
   // Protected code
} catch (ExceptionType1 e1) {
   // Catch block
} catch (ExceptionType2 e2) {
   // Catch block
} catch (ExceptionType3 e3) {
   // Catch block
}finally {
   // The finally block always executes.
}

Exemplo

public class ExcepTest {

   public static void main(String args[]) {
      int a[] = new int[2];
      try {
         System.out.println("Access element three :" + a[3]);
      } catch (ArrayIndexOutOfBoundsException e) {
         System.out.println("Exception thrown  :" + e);
      }finally {
         a[0] = 6;
         System.out.println("First element value: " + a[0]);
         System.out.println("The finally statement is executed");
      }
   }
}

Isso produzirá o seguinte resultado -

Resultado

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

Observe o seguinte -

  • Uma cláusula catch não pode existir sem uma instrução try.

  • Não é obrigatório ter cláusulas finally sempre que um bloco try / catch estiver presente.

  • O bloco try não pode estar presente sem a cláusula catch ou a cláusula finally.

  • Nenhum código pode estar presente entre os blocos try, catch e finally.

O teste com recursos

Geralmente, quando usamos quaisquer recursos como streams, conexões, etc., temos que fechá-los explicitamente usando finally block. No programa a seguir, estamos lendo dados de um arquivo usandoFileReader e o estamos fechando usando o bloco finally.

Exemplo

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReadData_Demo {

   public static void main(String args[]) {
      FileReader fr = null;		
      try {
         File file = new File("file.txt");
         fr = new FileReader(file); char [] a = new char[50];
         fr.read(a);   // reads the content to the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }finally {
         try {
            fr.close();
         } catch (IOException ex) {		
            ex.printStackTrace();
         }
      }
   }
}

try-with-resources, também conhecido como automatic resource management, é um novo mecanismo de manipulação de exceção que foi introduzido no Java 7, que fecha automaticamente os recursos usados ​​no bloco try catch.

Para usar esta instrução, você simplesmente precisa declarar os recursos necessários entre parênteses, e o recurso criado será fechado automaticamente no final do bloco. A seguir está a sintaxe da instrução try-with-resources.

Sintaxe

try(FileReader fr = new FileReader("file path")) {
   // use the resource
   } catch () {
      // body of catch 
   }
}

A seguir está o programa que lê os dados em um arquivo usando a instrução try-with-resources.

Exemplo

import java.io.FileReader;
import java.io.IOException;

public class Try_withDemo {

   public static void main(String args[]) {
      try(FileReader fr = new FileReader("E://file.txt")) {
         char [] a = new char[50];
         fr.read(a);   // reads the contentto the array
         for(char c : a)
         System.out.print(c);   // prints the characters one by one
      } catch (IOException e) {
         e.printStackTrace();
      }
   }
}

Os pontos a seguir devem ser mantidos em mente ao trabalhar com a instrução try-with-resources.

  • Para usar uma classe com a instrução try-with-resources, ela deve implementar AutoCloseable interface e o close() método dele é chamado automaticamente em tempo de execução.

  • Você pode declarar mais de uma classe na instrução try-with-resources.

  • Enquanto você declara várias classes no bloco try da instrução try-with-resources, essas classes são fechadas na ordem inversa.

  • Exceto que a declaração de recursos entre parênteses, tudo é igual ao bloco try / catch normal de um bloco try.

  • O recurso declarado em try é instanciado logo antes do início do bloco try.

  • O recurso declarado no bloco try é declarado implicitamente como final.

Exceções definidas pelo usuário

Você pode criar suas próprias exceções em Java. Mantenha os seguintes pontos em mente ao escrever suas próprias classes de exceção -

  • Todas as exceções devem ser filho de Throwable.

  • Se você deseja escrever uma exceção marcada que é automaticamente aplicada pela regra de tratamento ou declaração, você precisa estender a classe de exceção.

  • Se você deseja escrever uma exceção de tempo de execução, você precisa estender a classe RuntimeException.

Podemos definir nossa própria classe de exceção conforme abaixo -

class MyException extends Exception {
}

Você só precisa estender o pré-definido Exceptionclasse para criar sua própria exceção. Estas são consideradas exceções verificadas. Os seguintesInsufficientFundsExceptionclasse é uma exceção definida pelo usuário que estende a classe Exception, tornando-a uma exceção verificada. Uma classe de exceção é como qualquer outra classe, contendo campos e métodos úteis.

Exemplo

// File Name InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception {
   private double amount;
   
   public InsufficientFundsException(double amount) {
      this.amount = amount;
   }
   
   public double getAmount() {
      return amount;
   }
}

Para demonstrar o uso de nossa exceção definida pelo usuário, a seguinte classe CheckingAccount contém um métododraw () que lança uma InsufficientFundsException.

// File Name CheckingAccount.java
import java.io.*;

public class CheckingAccount {
   private double balance;
   private int number;
   
   public CheckingAccount(int number) {
      this.number = number;
   }
   
   public void deposit(double amount) {
      balance += amount;
   }
   
   public void withdraw(double amount) throws InsufficientFundsException {
      if(amount <= balance) {
         balance -= amount;
      }else {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   
   public double getBalance() {
      return balance;
   }
   
   public int getNumber() {
      return number;
   }
}

O programa BankDemo a seguir demonstra como chamar os métodos deposit () e saque () de CheckingAccount.

// File Name BankDemo.java
public class BankDemo {

   public static void main(String [] args) {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      
      try {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      } catch (InsufficientFundsException e) {
         System.out.println("Sorry, but you are short $" + e.getAmount());
         e.printStackTrace();
      }
   }
}

Compile todos os três arquivos acima e execute o BankDemo. Isso produzirá o seguinte resultado -

Resultado

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
         at CheckingAccount.withdraw(CheckingAccount.java:25)
         at BankDemo.main(BankDemo.java:13)

Exceções Comuns

Em Java, é possível definir duas categorias de exceções e erros.

  • JVM Exceptions- Essas são exceções / erros que são exclusiva ou logicamente lançados pela JVM. Exemplos: NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException.

  • Programmatic Exceptions- Essas exceções são lançadas explicitamente pelo aplicativo ou pelos programadores de API. Exemplos: IllegalArgumentException, IllegalStateException.