1. Múltiples catch: manejo de distintas excepciones
En programas reales, un mismo bloque de código puede lanzar distintos tipos de excepciones. Por ejemplo, al leer un archivo puede aparecer tanto FileNotFoundException como IOException, y al analizar datos — también NumberFormatException. Para manejar correctamente cada situación, en Java se pueden utilizar varios bloques catch seguidos.
Sintaxis
try {
// operaciones peligrosas
} catch (FileNotFoundException e) {
System.out.println("Archivo no encontrado: " + e.getMessage());
} catch (IOException e) {
System.out.println("Error de entrada/salida: " + e.getMessage());
} catch (NumberFormatException e) {
System.out.println("Formato de número no válido: " + e.getMessage());
}
Punto importante:
¡Los bloques catch deben ir de los más específicos a los más generales! Si primero escribes catch (Exception e), los demás bloques serán inalcanzables y el compilador dará un error.
¿Por qué es importante?
- Tratamiento específico: Puedes reaccionar de manera distinta ante diferentes errores (por ejemplo, proponer elegir otro archivo o repetir la entrada).
- Legibilidad: El código se vuelve más claro — se ve qué errores se esperan y cómo se manejan.
2. catch con varios tipos (multi-catch)
A veces distintos tipos de excepciones requieren el mismo manejo. Por ejemplo, quieres simplemente mostrar un mensaje de error o registrarlo, independientemente de si fue un error de lectura de archivo o de conversión de número.
Desde Java 7 existe la sintaxis de multi-catch, que permite agrupar varios tipos de excepciones en un mismo bloque:
try {
// operación peligrosa
} catch (IOException | NumberFormatException e) {
System.out.println("Error al procesar el archivo o el número: " + e.getMessage());
}
Cómo funciona:
- Los tipos de excepción se indican separados por la barra vertical (|).
- La variable e tendrá el tipo de la clase base más general (normalmente Exception).
- Dentro del bloque no se puede asignar un nuevo valor a la variable e — se considera final automáticamente.
Ejemplo:
try {
BufferedReader reader = new BufferedReader(new FileReader("numbers.txt"));
String line = reader.readLine();
int number = Integer.parseInt(line);
System.out.println("Número: " + number);
reader.close();
} catch (IOException | NumberFormatException e) {
System.out.println("Error: " + e.getMessage());
}
3. Buenas prácticas para el manejo de excepciones
No silencies las excepciones
Malo:
try {
// algo peligroso
} catch (Exception e) {
// ¡no hacemos nada!
}
¿Por qué es malo?
Un bloque catch vacío «silencia» el error: pierdes la información sobre la causa del fallo y el programa se vuelve difícil de depurar.
Mejor: como mínimo, muestra un mensaje o registra el error.
catch (Exception e) {
e.printStackTrace();
}
Si no puedes manejar la excepción — propágala:
catch (IOException e) {
throw e; // o throw new RuntimeException(e);
}
Lanza las excepciones más específicas
Si escribes tus propios métodos o API, lanza siempre excepciones lo más concretas posible, y no simplemente Exception o RuntimeException. Esto hace que tu código y tu interfaz sean más comprensibles para otros desarrolladores.
Ejemplo:
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Fondos insuficientes");
}
// ...
}
No uses las excepciones para controlar el flujo del programa
Las excepciones están pensadas para manejar situaciones excepcionales, es decir, errores que no deberían ocurrir en el curso normal del programa. No las uses para la lógica normal (por ejemplo, para salir de un bucle o comprobar condiciones).
Malo:
try {
while (true) {
String line = reader.readLine();
if (line == null) break;
// procesamiento de la línea
}
} catch (Exception e) {
// usamos una excepción para salir del bucle — ¡mal!
}
Bien:
String line;
while ((line = reader.readLine()) != null) {
// procesamiento de la línea
}
4. Ejemplos prácticos
Ejemplo: manejar distintos errores de forma diferente
try {
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
int number = Integer.parseInt(line);
System.out.println("Número: " + number);
reader.close();
} catch (FileNotFoundException e) {
System.out.println("¡Archivo no encontrado!");
} catch (IOException e) {
System.out.println("¡Error de lectura del archivo!");
} catch (NumberFormatException e) {
System.out.println("¡El archivo no contiene un número!");
}
Ejemplo: multi-catch para un manejo uniforme
try {
// algo peligroso
} catch (IOException | NumberFormatException e) {
System.out.println("Error: " + e.getMessage());
}
5. Matices importantes y errores comunes
Error n.º 1: Orden incorrecto de los bloques catch. Primero deben ir las excepciones más específicas y luego — las generales. De lo contrario, nunca se alcanzará el bloque más amplio.
Error n.º 2: Uso de tipos relacionados en un multi-catch. Por ejemplo, catch (IOException | Exception e) no compilará porque IOException hereda de Exception.
Error n.º 3: Intentar modificar la variable e en un multi-catch. En este caso e se considera final — no se le puede asignar un nuevo valor.
Error n.º 4: Bloques catch vacíos. Ignorar errores conduce a fallos «silenciosos». Como mínimo, muestra un mensaje o registra la excepción.
Error n.º 5: Atrapar todos los Exception indiscriminadamente. Usar catch (Exception e) oculta problemas reales y dificulta la depuración. Es mejor capturar solo las excepciones esperadas.
GO TO FULL VERSION