Java: excepciones

Una excepción (o evento excepcional) es un problema que surge durante la ejecución de un programa. Cuando unException ocurre, el flujo normal del programa se interrumpe y el programa / Aplicación termina de manera anormal, lo cual no se recomienda, por lo tanto, estas excepciones deben ser manejadas.

Una excepción puede ocurrir por muchas razones diferentes. A continuación se muestran algunos escenarios en los que se produce una excepción.

  • Un usuario ha introducido datos no válidos.

  • No se puede encontrar un archivo que debe abrirse.

  • Se perdió una conexión de red en medio de las comunicaciones o la JVM se quedó sin memoria.

Algunas de estas excepciones son causadas por un error del usuario, otras por un error del programador y otras por recursos físicos que han fallado de alguna manera.

Con base en estos, tenemos tres categorías de excepciones. Debe comprenderlos para saber cómo funciona el manejo de excepciones en Java.

  • Checked exceptions- Una excepción marcada es una excepción que es verificada (notificada) por el compilador en tiempo de compilación, también se denominan excepciones en tiempo de compilación. Estas excepciones no pueden simplemente ignorarse, el programador debe encargarse de (manejar) estas excepciones.

Por ejemplo, si usa FileReaderclass en su programa para leer datos de un archivo, si el archivo especificado en su constructor no existe, entonces se produce una FileNotFoundException y el compilador solicita al programador que maneje la excepción.

Ejemplo

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); 
   }
}

Si intenta compilar el programa anterior, obtendrá las siguientes excepciones.

Salida

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 - Dado que los métodos read() y close() de la clase FileReader lanza IOException, puede observar que el compilador notifica para manejar IOException, junto con FileNotFoundException.

  • Unchecked exceptions- Una excepción sin marcar es una excepción que ocurre en el momento de la ejecución. Estos también se denominan comoRuntime Exceptions. Estos incluyen errores de programación, como errores lógicos o uso inadecuado de una API. Las excepciones en tiempo de ejecución se ignoran en el momento de la compilación.

Por ejemplo, si se ha declarado una matriz de tamaño 5 en su programa, y tratando de llamar a la 6 ª elemento de la matriz a continuación, un ArrayIndexOutOfBoundsExceptionexception se produce.

Ejemplo

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

Si compila y ejecuta el programa anterior, obtendrá la siguiente excepción.

Salida

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at Exceptions.Unchecked_Demo.main(Unchecked_Demo.java:8)
  • Errors- No son excepciones en absoluto, sino problemas que surgen fuera del control del usuario o del programador. Los errores generalmente se ignoran en su código porque rara vez puede hacer algo al respecto. Por ejemplo, si se produce un desbordamiento de pila, surgirá un error. También se ignoran en el momento de la compilación.

Jerarquía de excepciones

Todas las clases de excepción son subtipos de la clase java.lang.Exception. La clase de excepción es una subclase de la clase Throwable. Aparte de la clase de excepción, hay otra subclase llamada Error que se deriva de la clase Throwable.

Los errores son condiciones anormales que ocurren en caso de fallas graves, estos no son manejados por los programas Java. Los errores se generan para indicar errores generados por el entorno de ejecución. Ejemplo: JVM no tiene memoria. Normalmente, los programas no pueden recuperarse de errores.

La clase Exception tiene dos subclases principales: clase IOException y clase RuntimeException.

A continuación se muestra una lista de las excepciones integradas de Java marcadas y no marcadas más comunes .

Métodos de excepciones

A continuación se muestra la lista de métodos importantes disponibles en la clase Throwable.

No Señor. Método y descripción
1

public String getMessage()

Devuelve un mensaje detallado sobre la excepción que se ha producido. Este mensaje se inicializa en el constructor Throwable.

2

public Throwable getCause()

Devuelve la causa de la excepción representada por un objeto Throwable.

3

public String toString()

Devuelve el nombre de la clase concatenada con el resultado de getMessage ().

4

public void printStackTrace()

Imprime el resultado de toString () junto con el seguimiento de la pila en System.err, el flujo de salida del error.

5

public StackTraceElement [] getStackTrace()

Devuelve una matriz que contiene cada elemento del seguimiento de la pila. El elemento en el índice 0 representa la parte superior de la pila de llamadas y el último elemento de la matriz representa el método en la parte inferior de la pila de llamadas.

6

public Throwable fillInStackTrace()

Rellena el seguimiento de la pila de este objeto Throwable con el seguimiento de la pila actual, agregando a cualquier información anterior en el seguimiento de la pila.

Captura de excepciones

Un método detecta una excepción utilizando una combinación de try y catchpalabras clave. Se coloca un bloque try / catch alrededor del código que podría generar una excepción. El código dentro de un bloque try / catch se conoce como código protegido, y la sintaxis para usar try / catch es similar a la siguiente:

Sintaxis

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

El código que es propenso a excepciones se coloca en el bloque try. Cuando ocurre una excepción, esa excepción se maneja mediante el bloque catch asociado a ella. Cada bloque de prueba debe ser seguido inmediatamente por un bloque de captura o por último.

Una declaración de captura implica declarar el tipo de excepción que está tratando de detectar. Si ocurre una excepción en el código protegido, se marca el bloque (o bloques) catch que sigue al intento. Si el tipo de excepción que ocurrió se enumera en un bloque de captura, la excepción se pasa al bloque de captura de la misma manera que se pasa un argumento a un parámetro de método.

Ejemplo

La siguiente es una matriz declarada con 2 elementos. A continuación, el código intenta acceder a la 3 rd elemento de la matriz, que se produce una excepción.

// 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");
   }
}

Esto producirá el siguiente resultado:

Salida

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

Múltiples bloques de captura

Un bloque de prueba puede ir seguido de varios bloques de captura. La sintaxis de varios bloques de captura se parece a la siguiente:

Sintaxis

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

Las declaraciones anteriores demuestran tres bloques de captura, pero puede tener cualquier número de ellos después de un solo intento. Si ocurre una excepción en el código protegido, la excepción se lanza al primer bloque catch de la lista. Si el tipo de datos de la excepción lanzada coincide con ExceptionType1, se detecta allí. De lo contrario, la excepción pasa a la segunda declaración de captura. Esto continúa hasta que la excepción se detecta o cae en todas las capturas, en cuyo caso el método actual detiene la ejecución y la excepción se lanza al método anterior en la pila de llamadas.

Ejemplo

Aquí hay un segmento de código que muestra cómo usar múltiples declaraciones 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;
}

Detectar varios tipos de excepciones

Desde Java 7, puede manejar más de una excepción usando un solo bloque de captura, esta característica simplifica el código. Así es como lo harías:

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

The Throws / Throw Palabras clave

Si un método no maneja una excepción marcada, el método debe declararlo usando el throwspalabra clave. La palabra clave throws aparece al final de la firma de un método.

Puede lanzar una excepción, ya sea una recién instanciada o una excepción que acaba de capturar, utilizando el throw palabra clave.

Intente comprender la diferencia entre throws y throw palabras clave, throws se usa para posponer el manejo de una excepción marcada y throw se usa para invocar una excepción explícitamente.

El siguiente método declara que lanza una RemoteException -

Ejemplo

import java.io.*;
public class className {

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

Un método puede declarar que arroja más de una excepción, en cuyo caso las excepciones se declaran en una lista separada por comas. Por ejemplo, el siguiente método declara que arroja una RemoteException y una InsufficientFundsException -

Ejemplo

import java.io.*;
public class className {

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

El bloque finalmente

El bloque finalmente sigue a un bloque try o un bloque catch. Un último bloque de código siempre se ejecuta, independientemente de la ocurrencia de una excepción.

El uso de un bloque finalmente le permite ejecutar cualquier declaración de tipo limpieza que desee ejecutar, sin importar lo que suceda en el código protegido.

Un bloque finalmente aparece al final de los bloques de captura y tiene la siguiente sintaxis:

Sintaxis

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

Ejemplo

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");
      }
   }
}

Esto producirá el siguiente resultado:

Salida

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

Tenga en cuenta lo siguiente:

  • Una cláusula catch no puede existir sin una declaración try.

  • No es obligatorio tener finalmente cláusulas siempre que haya un bloque try / catch.

  • El bloque try no puede estar presente sin la cláusula catch o la cláusula finalmente.

  • Ningún código puede estar presente entre los bloques try, catch, finalmente.

El intento con recursos

Generalmente, cuando usamos cualquier recurso como flujos, conexiones, etc., tenemos que cerrarlos explícitamente usando finalmente bloquear. En el siguiente programa, estamos leyendo datos de un archivo usandoFileReader y lo estamos cerrando usando finalmente bloquear.

Ejemplo

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, también conocido como automatic resource management, es un nuevo mecanismo de manejo de excepciones que se introdujo en Java 7, que cierra automáticamente los recursos utilizados dentro del bloque try catch.

Para usar esta declaración, simplemente necesita declarar los recursos requeridos entre paréntesis, y el recurso creado se cerrará automáticamente al final del bloque. A continuación se muestra la sintaxis de la declaración try-with-resources.

Sintaxis

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

A continuación se muestra el programa que lee los datos en un archivo usando la instrucción try-with-resources.

Ejemplo

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();
      }
   }
}

Se deben tener en cuenta los siguientes puntos al trabajar con la declaración try-with-resources.

  • Para usar una clase con declaración try-with-resources, debe implementar AutoCloseable interfaz y el close() Este método se invoca automáticamente en tiempo de ejecución.

  • Puede declarar más de una clase en la declaración try-with-resources.

  • Mientras declara varias clases en el bloque try de la declaración try-with-resources, estas clases se cierran en orden inverso.

  • Excepto la declaración de recursos entre paréntesis, todo es lo mismo que el bloque try / catch normal de un bloque try.

  • El recurso declarado en try se crea una instancia justo antes del inicio del bloque try.

  • El recurso declarado en el bloque try se declara implícitamente como final.

Excepciones definidas por el usuario

Puede crear sus propias excepciones en Java. Tenga en cuenta los siguientes puntos al escribir sus propias clases de excepción:

  • Todas las excepciones deben ser hijo de Throwable.

  • Si desea escribir una excepción marcada que se aplica automáticamente mediante la regla Handle o Declare, debe ampliar la clase Exception.

  • Si desea escribir una excepción en tiempo de ejecución, debe extender la clase RuntimeException.

Podemos definir nuestra propia clase de excepción de la siguiente manera:

class MyException extends Exception {
}

Solo necesita extender el predefinido Exceptionclass para crear su propia excepción. Se consideran excepciones marcadas. El seguimientoInsufficientFundsExceptionclass es una excepción definida por el usuario que amplía la clase Exception, convirtiéndola en una excepción comprobada. Una clase de excepción es como cualquier otra clase, que contiene campos y métodos útiles.

Ejemplo

// 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 demostrar el uso de nuestra excepción definida por el usuario, la siguiente clase CheckingAccount contiene un método retiro () que arroja una 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;
   }
}

El siguiente programa BankDemo demuestra cómo invocar los métodos de depósito () y retiro () de la cuenta corriente.

// 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 los tres archivos anteriores y ejecute BankDemo. Esto producirá el siguiente resultado:

Salida

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)

Excepciones comunes

En Java, es posible definir dos categorías de Excepciones y Errores.

  • JVM Exceptions- Estas son excepciones / errores que son lanzados exclusiva o lógicamente por la JVM. Ejemplos: NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException.

  • Programmatic Exceptions- Estas excepciones son lanzadas explícitamente por la aplicación o los programadores de API. Ejemplos: IllegalArgumentException, IllegalStateException.