Captura de excepciones
En el momento en que se produce una excepción se crea un objeto del tipo Exception correspondiente y se lanza la excepción. Esta puede ser capturada permitiendo realizar las acciones oportunas. Por ejemplo, si se produce una excepción de tipo IOException se creará una instancia de esa clase.
Los bloques try y catch
Una excepción tiene la siguiente forma:
try {
// Sección de código que puede lanzar una excepción
} catch (UnaExcepcion e) {
// Sección que maneja la excepción "UnaExcepcion"
} catch (OtraExcepcion e) {
// Sección que maneja la excepción "OtraExcepcion"
}
El bloque try delimita aquella o aquellas instrucciones donde se puede producir una excepción. Si esto sucede, el control del programa se transfiere al bloque catch definido para el tipo de excepción que se produjo, pasando como parámetro el objeto del tipo de la excepción que se creó.
El bloque catch define las instrucciones que se deberán ejecutar en caso de que se produzca un determinado tipo de excepción.
Consideraciones:
- No puede existir código entre el bloque
tryycatch. - Puede haber varios bloques
catch, pero no puede tener declarada la misma clase de excepción. - Aunque existan varios bloques
catch, solo se ejecutará uno de ellos. - Los
catchmás específicos deben estar situados por delante de los más genéricos en relación a la jerarquía de herencia.
Multi-catch
El multi-catch es una funcionalidad que permite capturar múltiples excepciones en un mismo catch, reduciendo la duplicación de código. En vez de escribir varios bloques catch, puedes capturar varias excepciones con un único catch, separándolas con |.
try {
// Código que puede lanzar diferentes excepciones
} catch (Excepcion1 | Excepcion2 | Excepcion3 e) {
// Manejo de las excepciones
}
El objeto e pertenece al tipo común de las excepciones capturadas y puede ser usado dentro del catch. El método getMessage() devuelve un mensaje con el error producido.
Vamos a ver un ejemplo donde intentamos dividir un número y acceder a un índice de una lista. Estos errores pueden generar ArithmeticException e IndexOutOfBoundsException.
public class Main {
public static void main(String[] args) {
try {
int resultado = 10 / 0; // Esto lanza ArithmeticException
System.out.println(resultado);
int[] numeros = {1, 2, 3};
System.out.println(numeros[5]); // Esto lanza IndexOutOfBoundsException
} catch (ArithmeticException | IndexOutOfBoundsException e) {
System.out.println("Ocurrió un error: " + e.getMessage());
}
}
}
Herencia en la captura de excepciones
Java organiza las excepciones en una jerarquía de clases, donde cada clase hereda comportamiento de su clase padre.
Si añadimos este código:
catch (Exception e) {
...
}
Estamos capturando cualquier excepción que sea o herede de Exception.
Por ejemplo:
try {
lanzarError();
} catch (IOException e) {
System.out.println("Error de entrada/salida");
} catch (Exception e) {
System.out.println("Error genérico");
}
// Este código fuerza que se produzca una excepción de tipo FileNotFoundException
void lanzarError() throws FileNotFoundException {
throw new FileNotFoundException("Archivo no encontrado");
}
FileNotFoundException hereda de IOException:
- El primer
catch (IOException e)va a capturar la excepción aunque no sea exactamente unIOException, sino una subclase. - Si no pusieras ese
catch, la excepción pasaría alcatch (Exception e).
Si hacemos esto:
try {
lanzarError();
} catch (Exception e) {
System.out.println("Error genérico");
} catch (IOException e) {
System.out.println("Error de E/S");
}
IOException es hija de Exception, entonces nunca se llega al segundo catch. La herencia hace que todo lo que sea IOException ya lo capture el primer bloque.
El bloque finally
Las excepciones pueden tener un tercer bloque no obligatorio: finally.
Este bloque siempre se ejecuta después del bloque try, independientemente de que si se lanza o no una excepción.
try {
// Sección de código que puede lanzar una excepción
} catch (UnaExcepcion e) {
// Sección que maneja la excepción "UnaExcepcion"
} catch (OtraExcepcion e) {
// Sección que maneja la excepción "OtraExcepcion"
} finally {
//Sección que se ejecuta siempre
}
La traza de la pila (stack trace)
El método printStackTrace() de Throwable muestra la traza de la pila (stack trace) del error, indicando en qué punto del código ocurrió la excepción. Esto ayuda mucho en la depuración.
public class EjemploPrintStackTrace {
public static void main(String[] args) {
try {
int resultado = 10 / 0; // Lanza ArithmeticException
} catch (Exception e) {
e.printStackTrace(); // Muestra la traza del error
}
}
}
Esto produce la siguiente salida:
java.lang.ArithmeticException: / by zero
at EjemploPrintStackTrace.main(EjemploPrintStackTrace.java:5)
- Indica que ocurrió una
ArithmeticExceptionporque se intentó dividir entre cero. - El error apareció en la línea 5 del archivo
EjemploPrintStackTrace.java.