Saltar al contenido principal

Excepciones y validación

Un dato previsible puede validarse con una condición. Una excepción comunica que el flujo normal no puede continuar como estaba previsto.

Validar o lanzar​

if (cantidad <= 0) {
System.out.println("La cantidad debe ser positiva");
}

No uses excepciones para sustituir un if sencillo. En cambio, convertir texto puede fallar aunque hayamos recibido una entrada:

try {
int edad = Integer.parseInt("veinte");
} catch (NumberFormatException error) {
System.out.println("La edad debe ser numérica");
}

Jerarquía básica​

Exception agrupa muchas situaciones recuperables. Las checked exceptions deben capturarse o declararse con throws; IOException es un ejemplo. Las unchecked exceptions heredan de RuntimeException, como NumberFormatException o IndexOutOfBoundsException, y suelen señalar datos inesperados o errores de programación.

Que una excepción sea unchecked no significa que deba ignorarse.

Capturar con un propósito​

try {
int posicion = Integer.parseInt(texto);
System.out.println(nombres.get(posicion));
} catch (NumberFormatException error) {
System.out.println("La posición no es un número");
} catch (IndexOutOfBoundsException error) {
System.out.println("La posición no existe");
}

Varios catch permiten respuestas específicas. Un multi-catch es útil si la recuperación es idéntica: catch (NumberFormatException | IndexOutOfBoundsException error).

Evita catch (Exception e) {}: silencia el problema y dificulta diagnosticarlo. Captura cuando puedas recuperarte, traducir el error, añadir contexto o comunicarlo correctamente; si no, propagar suele ser mejor.

throw y throws​

throw lanza una excepción concreta. throws declara que un método puede propagarla:

public static int leerCantidad(String texto) {
int cantidad = Integer.parseInt(texto);
if (cantidad < 0) {
throw new IllegalArgumentException("Cantidad negativa");
}
return cantidad;
}

Las excepciones unchecked pueden propagarse aunque no aparezcan en throws. Es legal declararlas, pero normalmente no es necesario. El siguiente ejemplo, en cambio, usa SaldoInsuficienteException: al ser checked, debe capturarse o declararse.

Excepción de dominio​

class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException(String mensaje) {
super(mensaje);
}
}

class Cuenta {
private double saldo;
public Cuenta(double saldo) { this.saldo = saldo; }

public void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad <= 0) throw new IllegalArgumentException("Cantidad no positiva");
if (cantidad > saldo) {
throw new SaldoInsuficienteException("Saldo disponible: " + saldo);
}
saldo -= cantidad;
}
}

La cantidad no positiva incumple el contrato del método; el saldo insuficiente es una situación de dominio que el llamador puede gestionar.

finally​

El bloque finally se ejecuta normalmente haya o no excepción y se utilizaba para liberar recursos:

try {
System.out.println("Operación");
} finally {
System.out.println("Limpieza final");
}

En JV3-06 aprenderemos try-with-resources, que evita cierres manuales y expresa mejor esa responsabilidad.

Ejemplo completo​

class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException(String mensaje) { super(mensaje); }
}
class Cuenta {
private double saldo;
public Cuenta(double saldo) { this.saldo = saldo; }
public void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad <= 0) throw new IllegalArgumentException("Cantidad no positiva");
if (cantidad > saldo) throw new SaldoInsuficienteException("Saldo insuficiente");
saldo -= cantidad;
}
}
public class ExcepcionesDemo {
public static void main(String[] args) {
Cuenta cuenta = new Cuenta(50);
try {
cuenta.retirar(70);
} catch (SaldoInsuficienteException error) {
System.out.println(error.getMessage());
}
try {
System.out.println(Integer.parseInt("12"));
} catch (NumberFormatException error) {
System.out.println("Número inválido");
} finally {
System.out.println("Fin del intento");
}
}
}

Errores habituales​

  • Usar excepciones para condiciones esperadas fáciles de validar.
  • Capturar demasiado arriba o demasiado pronto.
  • Silenciar errores.
  • Lanzar una excepción sin mensaje útil.
  • Confundir una regla de dominio con un fallo del programador.

Practica: cupo agotado​

Enunciado y solución​

Crea una checked exception CupoAgotadoException. Un método reservar(int libres) la lanza si no quedan plazas. En main, captura el error e informa al usuario.

class CupoAgotadoException extends Exception {
public CupoAgotadoException() { super("No quedan plazas"); }
}
public class ReservasDemo {
static void reservar(int libres) throws CupoAgotadoException {
if (libres <= 0) throw new CupoAgotadoException();
System.out.println("Reserva creada");
}
public static void main(String[] args) {
try { reservar(0); }
catch (CupoAgotadoException error) { System.out.println(error.getMessage()); }
}
}

La captura está en el límite que sabe comunicar el resultado; la regla permanece cerca de la operación.