Las excepciones son el mecanismo de Java para gestionar errores y situaciones excepcionales. Un buen manejo de excepciones hace el código más robusto y más fácil de depurar.
Jerarquía de excepciones
Todas las excepciones heredan de Throwable. Hay dos ramas principales:
- Error: problemas graves de la JVM (
OutOfMemoryError,StackOverflowError). No se deben capturar en código de aplicación. - Exception: situaciones que el código puede manejar.
- Checked (comprobadas en compilación): heredan de
Exceptionpero no deRuntimeException. El compilador obliga a capturarlas o declararlas conthrows. Ejemplo:IOException,SQLException. - Unchecked (no comprobadas): heredan de
RuntimeException. Indican errores de programación. Ejemplo:NullPointerException,ArrayIndexOutOfBoundsException,IllegalArgumentException.
- Checked (comprobadas en compilación): heredan de
try / catch / finally
public int dividir(int a, int b) {
try {
return a / b;
} catch (ArithmeticException e) {
System.err.println("División por cero: " + e.getMessage());
return 0;
} finally {
// Siempre se ejecuta, haya o no excepción
System.out.println("Operación finalizada.");
}
}
Se pueden capturar múltiples tipos en un solo catch (Java 7+):
try {
// código que puede lanzar varias excepciones
} catch (IOException | SQLException e) {
System.err.println("Error de I/O o BD: " + e.getMessage());
}
try-with-resources (Java 7+)
Cualquier objeto que implemente AutoCloseable se cierra automáticamente al salir del bloque try, incluso si hay excepción. Elimina la necesidad de cerrar recursos manualmente en el finally:
// Sin try-with-resources (propenso a resource leaks)
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("fichero.txt"));
String linea = br.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) try { br.close(); } catch (IOException e) {}
}
// Con try-with-resources (limpio y seguro)
try (var br = new BufferedReader(new FileReader("fichero.txt"))) {
String linea = br.readLine();
System.out.println(linea);
} catch (IOException e) {
System.err.println("Error leyendo fichero: " + e.getMessage());
}
Lanzar excepciones: throw y throws
public void setEdad(int edad) {
if (edad < 0 || edad > 150) {
throw new IllegalArgumentException("Edad inválida: " + edad);
}
this.edad = edad;
}
// Declarar checked exception en la firma
public String leerFichero(String ruta) throws IOException {
return Files.readString(Path.of(ruta));
}
Excepciones personalizadas
// Excepción de negocio (unchecked)
public class SaldoInsuficienteException extends RuntimeException {
private final double saldoActual;
private final double importeSolicitado;
public SaldoInsuficienteException(double saldoActual, double importeSolicitado) {
super(String.format("Saldo insuficiente: tiene %.2f, necesita %.2f",
saldoActual, importeSolicitado));
this.saldoActual = saldoActual;
this.importeSolicitado = importeSolicitado;
}
public double getSaldoActual() { return saldoActual; }
public double getImporteSolicitado() { return importeSolicitado; }
}
// Uso
public void retirar(double importe) {
if (importe > saldo) {
throw new SaldoInsuficienteException(saldo, importe);
}
saldo -= importe;
}
Buenas prácticas
- Captura la excepción más específica posible, no
Exceptiongenérica. - Nunca captures y silencies:
catch (Exception e) {}es un antipatrón. - Usa excepciones unchecked (
RuntimeException) para errores de programación y de negocio. Las checked son para condiciones recuperables externas (fichero no encontrado, red caída). - Incluye información útil en el mensaje: qué ocurrió, con qué valor, dónde.
- Cierra siempre los recursos con try-with-resources.
