Manejo de excepciones en Java

En Java, el manejo de excepciones es un concepto de programación importante que se utiliza para manejar errores o situaciones anormales que pueden ocurrir durante la ejecución de un programa.

Las excepciones son algunos errores en el programa, pero no todos los errores son excepciones, y los errores a veces se pueden evitar.

Por ejemplo, si a tu código le falta un punto y coma, el resultado de la ejecución será un mensaje de errorjava.lang.Error, si usasSystem.out.println(11/0), entonces es porque usaste0como divisor, se lanzarájava.lang.ArithmeticExceptionla excepción.

Hay muchas razones por las que ocurren las excepciones, que generalmente se dividen en las siguientes categorías:

  • El usuario ingresó datos no válidos.
  • El archivo que se va a abrir no existe.
  • La conexión se interrumpe durante la comunicación de red, o la memoria JVM se desborda.

Algunas de estas excepciones son causadas por errores del usuario, otras por errores del programa, y algunas otras por errores físicos.

Para entender cómo funciona el manejo de excepciones en Java, necesitas dominar los siguientes tres tipos de excepciones:

  • Excepción comprobada:La excepción comprobada más representativa es la causada por errores o problemas del usuario; estas excepciones obligan al programador a manejarlas en tiempo de compilación. Por ejemplo, al intentar abrir un archivo que no existe, ocurre una excepción; estas excepciones no pueden simplemente ignorarse en tiempo de compilación.

    Este tipo de excepciones normalmente usatry-catchbloques para capturar y manejar la excepción, o usar en la declaración del métodothrowscláusulas para declarar las excepciones que el método puede lanzar.

    try {
        // 可能会抛出异常的代码
    } catch (IOException e) {
        // 处理异常的代码
    }

    O bien:

    public void readFile() throws IOException {
        // 可能会抛出IOException的代码
    }
  • Excepción de tiempo de ejecución:Estas excepciones no se exigen en tiempo de compilación; generalmente son causadas por errores en el programa, como NullPointerException, ArrayIndexOutOfBoundsException, etc. Este tipo de excepciones se pueden manejar de forma opcional, pero no es obligatorio.

    try {
        // 可能会抛出异常的代码
    } catch (NullPointerException e) {
        // 处理异常的代码
    }
  • Error:Los errores no son excepciones, sino problemas que escapan al control del programador; los errores generalmente se ignoran en el código. Por ejemplo, cuando ocurre un desbordamiento de pila, se produce un error, y tampoco se pueden detectar en la compilación.

Java proporciona las siguientes palabras clave y clases para admitir el manejo de excepciones:

  • try: se utiliza para envolver bloques de código que pueden lanzar excepciones.
  • catch: se utiliza para capturar la excepción y manejar el bloque de código de la excepción.
  • finally: se utiliza para incluir bloques de código que deben ejecutarse independientemente de si ocurre una excepción.
  • throw: se utiliza para lanzar excepciones manualmente.
  • throws: se utiliza para especificar en la declaración del método las excepciones que el método puede lanzar.
  • ExceptionClase: es la clase padre de todas las clases de excepción; proporciona algunos métodos para obtener información de la excepción, comogetMessage()、printStackTrace()etcétera.

Jerarquía de la clase Exception

Todas las clases de excepción son subclases que heredan de la clase java.lang.Exception.

La clase Exception es una subclase de la clase Throwable. Además de la clase Exception, Throwable también tiene una subclase Error.

Los programas Java normalmente no capturan errores. Los errores generalmente ocurren en fallas graves y están fuera del ámbito de manejo del programa Java.

Error se utiliza para indicar errores que ocurren en el entorno de ejecución.

Por ejemplo, desbordamiento de memoria de la JVM. Generalmente, el programa no se recupera de los errores.

La clase de excepción tiene dos subclases principales: la clase IOException y la clase RuntimeException.

Entre las clases integradas de Java (que se explicarán a continuación), se encuentran la mayoría de las excepciones comprobadas y no comprobadas de uso común.


Clases de excepción integradas en Java

El lenguaje Java define algunas clases de excepción en el paquete estándar java.lang.

Las subclases de las clases de excepción de tiempo de ejecución estándar son las clases de excepción más comunes. Debido a que el paquete java.lang se carga de forma predeterminada en todos los programas Java, la mayoría de las excepciones heredadas de las clases de excepción de tiempo de ejecución se pueden usar directamente.

Java también define otras excepciones según las distintas bibliotecas de clases; la siguiente tabla enumera las excepciones no comprobadas de Java.

Excepción Descripción
ArithmeticException Se lanza esta excepción cuando se presenta una condición de operación anormal. Por ejemplo, cuando un entero se 'divide por cero', se lanza una instancia de esta clase.
ArrayIndexOutOfBoundsException Excepción que se lanza al acceder a un arreglo con un índice ilegal. Si el índice es negativo o mayor o igual que el tamaño del arreglo, el índice es ilegal.
ArrayStoreException Excepción que se lanza al intentar almacenar un objeto de tipo incorrecto en un arreglo de objetos.
ClassCastException Se lanza esta excepción cuando se intenta convertir un objeto a una subclase de la que no es instancia.
IllegalArgumentException La excepción lanzada indica que se ha pasado un argumento ilegal o incorrecto a un método.
IllegalMonitorStateException La excepción lanzada indica que un hilo ha intentado esperar el monitor de un objeto, o intentar notificar a otros hilos que están esperando el monitor de un objeto, sin poseer el monitor especificado.
IllegalStateException Señal generada cuando se llama a un método en un momento ilegal o inapropiado. En otras palabras, el entorno Java o la aplicación Java no se encuentra en el estado adecuado requerido para la operación solicitada.
IllegalThreadStateException Excepción que se lanza cuando un hilo no se encuentra en el estado adecuado requerido para la operación solicitada.
IndexOutOfBoundsException Indica que un índice de ordenación (por ejemplo, para arreglos, cadenas o vectores) está fuera de rango.
NegativeArraySizeException Si la aplicación intenta crear un arreglo con tamaño negativo, se lanza esta excepción.
NullPointerException Cuando la aplicación intenta usar en un lugar donde se necesita un objetonull, se lanza esta excepción.
NumberFormatException Cuando la aplicación intenta convertir una cadena a un tipo numérico, pero la cadena no puede convertirse al formato adecuado, se lanza esta excepción.
SecurityException Excepción lanzada por el administrador de seguridad, que indica una violación de seguridad.
StringIndexOutOfBoundsException Esta excepción es lanzada porStringmétodo, e indica que el índice es negativo o está fuera del tamaño de la cadena.
UnsupportedOperationException Cuando no se admite la operación solicitada, se lanza esta excepción.

La siguiente tabla enumera las clases de excepción comprobadas definidas por Java en el paquete java.lang.

Excepción Descripción
ClassNotFoundException Cuando la aplicación intenta cargar una clase y no puede encontrar la clase correspondiente, se lanza esta excepción.
CloneNotSupportedException Cuando se llamaObjectde la claseclonemétodo para clonar un objeto, pero la clase del objeto no puede implementarCloneableinterfaz, se lanza esta excepción.
IllegalAccessException Cuando se deniega el acceso a una clase, se lanza esta excepción.
InstantiationException Cuando se intenta usarClassde la clasenewInstancemétodo para crear una instancia de una clase, pero el objeto de clase especificado no puede ser instanciado porque es una interfaz o una clase abstracta, se lanza esta excepción.
InterruptedException Cuando un hilo es interrumpido por otro hilo, se lanza esta excepción.
NoSuchFieldException La variable solicitada no existe.
NoSuchMethodException El método solicitado no existe.

Métodos de excepción

La siguiente lista son los métodos principales de la clase Throwable:

N.º Método y descripción
1 public String getMessage()
Devuelve información detallada sobre la excepción ocurrida. Este mensaje se inicializa en el constructor de la clase Throwable.
2 public Throwable getCause()
Devuelve un objeto Throwable que representa la causa de la excepción.
3 public String toString()
Devuelve una breve descripción de este Throwable.
4 public void printStackTrace()
Imprime este Throwable y su rastreo de pila en el flujo de error estándar.
5 public StackTraceElement [] getStackTrace()
Devuelve una matriz que contiene los niveles de la pila. El elemento con índice 0 representa la cima de la pila, y el último elemento representa el fondo de la pila de llamadas al método.
6 public Throwable fillInStackTrace()
Llena los niveles de pila del objeto Throwable con los niveles de pila de llamadas actuales, agregándolos a cualquier información anterior de los niveles de pila.

Capturar excepciones

Se pueden capturar excepciones usando las palabras clave try y catch. El bloque de código try/catch se coloca donde es probable que ocurra la excepción.

El código dentro del bloque try/catch se denomina código protegido. La sintaxis para usar try/catch es la siguiente:

try
{
   // 程序代码
}catch(ExceptionName e1)
{
   //Catch 块
}

La declaración catch contiene la declaración del tipo de excepción que se va a capturar. Cuando se produce una excepción en el bloque de código protegido, se examina el bloque catch que sigue a try.

Si la excepción producida está incluida en el bloque catch, se pasa a dicho bloque catch, de la misma manera que se pasa un argumento a un método.

Ejemplo

En el siguiente ejemplo se declara un array con dos elementos; cuando el código intenta acceder al cuarto elemento del array, se lanza una excepción.

Código del archivo ExcepTest.java:

//Nombre del archivo: 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"); } }

El resultado de compilar y ejecutar el código anterior es el siguiente:

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

Bloques de captura múltiple

La situación en la que un bloque try va seguido de varios bloques catch se denomina captura múltiple.

La sintaxis de la captura múltiple es la siguiente:

try{ //Código de programa }catch(Tipo de excepción1Nombre de la variable de excepción1){ //Código de programa }catch(Tipo de excepción2Nombre de la variable de excepción2){ //Código de programa }catch(Tipo de excepción3Nombre de la variable de excepción3){ //Código de programa }

El fragmento de código anterior contiene 3 bloques catch.

Se puede añadir cualquier número de bloques catch después de la sentencia try.

Si se produce una excepción en el código protegido, la excepción se lanza al primer bloque catch.

Si el tipo de datos de la excepción lanzada coincide con ExceptionType1, se capturará aquí.

Si no coincide, se pasará al segundo bloque catch.

Y así sucesivamente, hasta que la excepción sea capturada o pase por todos los bloques catch.

Ejemplo

Este ejemplo muestra cómo usar múltiples try/catch.

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

Captura combinada de múltiples excepciones

Desde Java 7, se introdujo una sintaxis más concisa: la captura combinada de múltiples excepciones, que permite manejar varias excepciones sin relación de herencia con un solo bloque catch.

try {
    // 可能抛出多个不同类型异常的代码
} catch (异常类型1 | 异常类型2 | 异常类型3 异常变量) {
    // 统一处理
}

Es necesario tener en cuenta:

  • Los tipos de excepción 1, los tipos de excepción 2, etc., no pueden tener relación de herencia; de lo contrario, se producirá un error de compilación.
  • El nombre de la variable de excepción es una variable de referencia común a estas tres excepciones, por lo que dentro del bloque catch no se pueden llamar a sus métodos específicos.
  • El compilador inferirá que el tipo de esta variable de excepción es el supertipo común más cercano de estas excepciones (por ejemplo, Exception o IOException).

Excepciones como las siguientes, que no se heredan entre sí, pueden lanzar simultáneamente SQLException e IOException:

Ejemplo

try {
    // También puede lanzar excepciones SQL e IO.
    doSomething();
} catch (SQLException | IOException e) {
    System.out.println(¡Ha ocurrido una excepción SQL e IO!);
    e.printStackTrace();
}

Ejemplo incorrecto (no se pueden combinar excepciones de clase padre e hija):

// 错误写法!
catch (FileNotFoundException | IOException e) {
    // 会导致编译错误,因为 FileNotFoundException 是 IOException 的子类
}

FileNotFoundException es una subclase de IOException, por lo que esta sintaxis en realidad no es válida y provocará un error de compilación.


Palabras clave throws/throw

En Java,throwythrowslas palabras clave se utilizan para manejar excepciones.

throwLa palabra clave throw se utiliza para lanzar una excepción en el código, ythrowsla palabra clave throws se utiliza en la declaración de un método para especificar los tipos de excepción que podría lanzar.

Palabra clave throw

throwEsta palabra clave se utiliza para lanzar una excepción en el método actual.

Normalmente, cuando el código llega a una condición en la que no puede continuar ejecutándose con normalidad, se puede usarthrowla palabra clave throw para lanzar una excepción, con el fin de informar al llamador del estado de ejecución actual del código.

Por ejemplo, en el siguiente código, se comprueba si num es menor que 0 dentro del método; si es así, se lanza una excepción IllegalArgumentException.

Ejemplo

public void checkNumber(int num) {
  if (num < 0) {
    throw new IllegalArgumentException("Number must be positive");
  }
}

Palabra clave throws

throwsEsta palabra clave se utiliza en la declaración de un método para especificar las excepciones que el método puede lanzar. Cuando se lanza una excepción del tipo especificado dentro del método, dicha excepción se pasa al código que llama al método, donde se maneja.

Por ejemplo, en el siguiente código, cuando se produce una excepción IOException dentro del método readFile, dicha excepción se pasa al código que llama al método. En el código que llama a este método, se debe capturar o declarar el manejo de la excepción IOException.

Ejemplo

public void readFile(String filePath) throws IOException {
  BufferedReader reader = new BufferedReader(new FileReader(filePath));
  String line = reader.readLine();
  while (line != null) {
    System.out.println(line);
    line = reader.readLine();
  }
  reader.close();
}

Un método puede declarar que lanza varias excepciones; las excepciones se separan con comas.

Por ejemplo, el siguiente método declara que lanza RemoteException e InsufficientFundsException:

import java.io.*; public class className { public void withdraw(double amount) throws RemoteException, InsufficientFundsException { // Method implementation } //Remainder of class definition }

Palabra clave finally

La palabra clave finally se utiliza para crear un bloque de código que se ejecuta después del bloque try.

Tanto si se produce una excepción como si no, el código del bloque finally siempre se ejecuta.

En el bloque finally se pueden ejecutar sentencias de tipo limpieza, cierre o finalización.

El bloque finally aparece al final de los bloques catch; la sintaxis es la siguiente:

try{ //Código de programa }catch(Tipo de excepción1Nombre de la variable de excepción1){ //Código de programa }catch(Tipo de excepción2Nombre de la variable de excepción2){ //Código de programa }finally{ //Código de programa }

Ejemplo

Código del archivo ExcepTest.java:

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

El resultado de compilar y ejecutar el ejemplo anterior es el siguiente:

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

Tenga en cuenta lo siguiente:

  • catch no puede existir independientemente de try.
  • Añadir un bloque finally después de try/catch no es obligatorio.
  • Después de un bloque try no puede haber ni un bloque catch ni un bloque finally.
  • Entre los bloques try, catch y finally no se puede añadir ningún código.

try-with-resources

Después de JDK7, Java añadiótry-with-resourceuna construcción sintáctica, destinada a gestionar automáticamente los recursos, asegurando que los recursos se cierren a tiempo después de su uso, evitando fugas de recursos.

try-with-resources es un mecanismo de manejo de excepciones que cierra automáticamente los recursos declarados en el bloque try, sin necesidad de cerrarlos explícitamente en un bloque finally.

En la sentencia try-with-resources, solo necesita declarar los recursos después de la palabra clave try y luego un bloque de código. Tanto si la operación dentro del bloque tiene éxito como si no, los recursos se cierran automáticamente después de que el bloque try haya terminado de ejecutarse.

try (resource declaration) {
  // 使用的资源
} catch (ExceptionType e1) {
  // 异常块
}

En la sintaxis anterior, try se utiliza para declarar e instanciar recursos, y catch para manejar todas las excepciones que puedan surgir al cerrar los recursos.

Nota:La sentencia try-with-resources cierra todos los recursos que implementan la interfaz AutoCloseable.

Ejemplo

import java.io.*;

public class EjemploTest {

    public static void main(String[] args) {
    String line;
        try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            while ((line = br.readLine()) != null) {
                System.out.println("Line =>"+line);
            }
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        }
    }
}

El resultado de salida del ejemplo anterior es:

IOException in try block =>test.txt (No such file or directory)

En el ejemplo anterior, instanciamos un objeto BufferedReader para leer datos del archivo test.txt.

Al declarar e instanciar el objeto BufferedReader en la sentencia try-with-resources, los recursos se cierran al finalizar la ejecución, sin necesidad de considerar si la sentencia try se ejecutó con normalidad o lanzó una excepción.

Si se produce una excepción, se puede usar catch para manejarla.

Observe también, en lugar de usartry-with-resourcesy cambiarlo afinallypara cerrar los recursos, la cantidad total de código es mucho mayor y además resulta más complejo y engorroso:

Ejemplo

import java.io.*;

class EjemploTest {
    public static void main(String[] args) {
        BufferedReader br = null;
        String line;

        try {
            System.out.println("Entering try block");
            br = new BufferedReader(new FileReader("test.txt"));
            while ((line = br.readLine()) != null) {
            System.out.println("Line =>"+line);
            }
        } catch (IOException e) {
            System.out.println("IOException in try block =>" + e.getMessage());
        } finally {
            System.out.println("Entering finally block");
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                System.out.println("IOException in finally block =>"+e.getMessage());
            }
        }
    }
}

El resultado de salida del ejemplo anterior es:

Entering try block
IOException in try block =>test.txt (No such file or directory)
Entering finally block

try-with-resources para manejar múltiples recursos

En una sentencia try-with-resources se pueden declarar múltiples recursos; para ello se utiliza el punto y coma;para separar cada recurso:

Ejemplo

import java.io.*;
import java.util.*;
class EjemploTest {
    public static void main(String[] args) throws IOException{
        try (Scanner scanner = new Scanner(new File("testRead.txt"));
            PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
            while (scanner.hasNext()) {
                writer.print(scanner.nextLine());
            }
        }
    }
}

El ejemplo anterior utiliza un objeto Scanner para leer una línea del archivo testRead.txt y escribirla en un nuevo archivo testWrite.txt.

Cuando se declaran múltiples recursos,try-with-resourcesla sentencia cierra estos recursos en orden inverso. En este ejemplo, el objeto PrintWriter se cierra primero y luego el objeto Scanner.

Consulte más contenido en:


Declarar excepciones personalizadas

En Java puedes definir excepciones personalizadas. Al escribir tu propia clase de excepción, ten en cuenta los siguientes puntos.

  • Todas las excepciones deben ser subclases de Throwable.
  • Si deseas escribir una excepción comprobada, debes heredar de la clase Exception.
  • Si quieres escribir una excepción de runtime, debes heredar de la clase RuntimeException.

Puedes definir tu propia clase de excepción de la siguiente manera:

class MyException extends Exception{ }

La clase de excepción creada simplemente heredando de la clase Exception es una excepción comprobada.

La siguiente clase InsufficientFundsException es una clase de excepción definida por el usuario que hereda de Exception.

Una clase de excepción, como cualquier otra clase, contiene variables y métodos.

Ejemplo

El siguiente ejemplo es una simulación de una cuenta bancaria, que se identifica mediante el número de la tarjeta bancaria, y puede realizar operaciones de depósito y retiro.

Código del archivo InsufficientFundsException.java:

//Nombre del archivo: InsufficientFundsException.java import java.io.*; //Clase de excepción personalizada, hereda de la clase Exception. public class InsufficientFundsException extends Exception { //Aquí, amount se utiliza para almacenar el dinero que falta cuando se produce una excepción (cuando se retira más dinero del saldo). private double amount; public InsufficientFundsException(double amount) { this.amount = amount; } public double getAmount() { return amount; } }

Para mostrar cómo usar nuestra clase de excepción personalizada,

En la siguiente clase CheckingAccount se incluye un método withdraw() que lanza una excepción InsufficientFundsException.

Código del archivo CheckingAccount.java:

//Nombre del archivo: CheckingAccount.java import java.io.*; //Esta clase simula una cuenta bancaria. public class CheckingAccount { //balance es el saldo, number es el número de tarjeta. private double balance; private int number; public CheckingAccount(int number) { this.number = number; } //Método: depositar dinero. public void deposit(double amount) { balance += amount; } //Método: retirar dinero. public void withdraw(double amount) throws InsufficientFundsException { if(amount <= balance) { balance -= amount; } else { double needs = amount - balance; throw new InsufficientFundsException(needs); } } //Método: devolver el saldo. public double getBalance() { return balance; } //Método: devolver el número de tarjeta. public int getNumber() { return number; } }

El siguiente programa BankDemo demuestra cómo llamar a los métodos deposit() y withdraw() de la clase CheckingAccount.

Código del archivo BankDemo.java:

//Nombre del archivo: 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 el programa BankDemo; el resultado es el siguiente:

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 se definen dos tipos de excepciones y errores.

  • JVM(JavaMáquina virtual)Excepciones:Excepciones o errores lanzados por la JVM. Por ejemplo: la clase NullPointerException, la clase ArrayIndexOutOfBoundsException, la clase ClassCastException.
  • Excepciones a nivel de programa:Excepciones lanzadas por el programa o por programas de API. Por ejemplo: la clase IllegalArgumentException, la clase IllegalStateException.

Mejores prácticas para el manejo de excepciones

  • Capturar las excepciones en el lugar adecuado y manejarlas apropiadamente para garantizar la estabilidad y fiabilidad del programa.
  • Evite capturar excepciones en exceso; intente capturar con precisión tipos específicos de excepciones.
  • Utilice el bloque finally para liberar recursos, por ejemplo, cerrar archivos o conexiones de base de datos, para garantizar la liberación correcta de los recursos.
  • Priorice el manejo de excepciones comprobadas y evite convertir excepciones comprobadas en no comprobadas.

El manejo de excepciones es una parte importante de la escritura de aplicaciones Java robustas; manejar las excepciones razonablemente puede mejorar la mantenibilidad y fiabilidad del programa.

Otras extensiones