Excepciones en Java — ejercicios con un conversor, fechas y un banco (y el try que puse en el sitio equivocado)
Tenía que sumar las notas de un grupo de 30 alumnos que venían como texto. Como ya sabía que alguna podía venir mal escrita, metí el bucle dentro de un try y capturé la NumberFormatException. El programa no se caía, imprimía «Nota ignorada» una vez… y la suma salía muy baja. Tardé un buen rato en verlo: el try estaba fuera del bucle. En cuanto la tercera nota falló, el resto del try se saltó, y el resto del try era el bucle entero. Las otras 27 notas ni se miraron.
En estos ejercicios vas a decidir dónde va cada try, qué excepción lanzar en cada caso, cómo traducir un error técnico a uno que se entienda y cómo diseñar tu propia familia de excepciones. Antes de las soluciones, tres fragmentos con trampa (el primero es justo mi try mal colocado). Si necesitas repasar antes de empezar, tienes la teoría y la práctica del tema.
Tabla de Contenidos
Cómo usar este artículo
- Lee el enunciado y la salida esperada.
- Inténtalo al menos 15 minutos antes de abrir la pista.
- Antes de escribir código, decide para cada error: ¿lo lanzo yo o lo lanza Java? ¿Lo trato aquí o lo dejo subir?
- Compara con la solución y lee el porqué de cada decisión.
No usamos Scanner para que las salidas sean exactas: las «entradas del usuario» van en arrays. La lógica es la misma que si las leyeras del teclado.
Nivel 1 — Básico: un conversor seguro
Enunciado. Crea la clase Conversor con dos métodos static:
int aEntero(String texto, int porDefecto): convierte el texto (quitando espacios) a entero; si no se puede, devuelveporDefecto.int pedirEntre(String[] entradas, int min, int max): simula pedir un número al usuario. Recorre las entradas en orden; para cada una que no sea un entero o esté fuera de[min, max], imprime el motivo y pasa a la siguiente. Devuelve la primera válida. Si ninguna lo es, lanza unaIllegalStateExceptioncon el número de intentos.
Pruebas y salida esperada:
System.out.println(aEntero("42", 0) + " " + aEntero(" 7 ", 0) + " " + aEntero("3.5", -1) + " " + aEntero("", -1));
System.out.println("Edad:");
int edad = pedirEntre(new String[]{"veinte", "-3", "150", " 19 "}, 0, 120);
System.out.println("Nota:");
try {
pedirEntre(new String[]{"diez", "11"}, 0, 10);
} catch (IllegalStateException e) {
System.out.println(" Error: " + e.getMessage());
}
System.out.println("Edad guardada: " + edad);
42 7 -1 -1 Edad: "veinte": no es un número entero "-3": fuera de rango [0, 120] "150": fuera de rango [0, 120] Aceptado 19 en el intento 4 Nota: "diez": no es un número entero "11": fuera de rango [0, 10] Error: ninguna entrada válida en 2 intentos Edad guardada: 19
En pedirEntre, el try va dentro del for. El rango no lanza ninguna excepción por sí solo: compruébalo con un if y usa continue para pasar al siguiente intento.
Nivel 2 — Intermedio: una Fecha que no se deja crear mal
Enunciado. Crea la clase Fecha (día, mes y año, private final):
- El constructor lanza
IllegalArgumentExceptionsi el mes no está entre 1 y 12 o si el día no existe en ese mes, con un mensaje que explique por qué (incluye los días que tiene el mes). static boolean esBisiesto(int anio): divisible entre 4 y no entre 100, o divisible entre 400.static int diasDelMes(int mes, int anio).static Fecha parse(String texto)para textosdd/mm/aaaa. Si no hay tres partes, lanzaIllegalArgumentException; si alguna parte no es un número, captura laNumberFormatExceptiony lanza en su lugar unaIllegalArgumentExceptioncon la original como causa.toString()con el formato12/10/2026.
Pruebas y salida esperada:
String[] textos = {"12/10/2026", "29/02/2024", "29/02/2026", "31/04/2026", "15/13/2026", "1-1-2026", "aa/10/2026"};
for (String t : textos) {
try {
System.out.println("OK " + Fecha.parse(t));
} catch (IllegalArgumentException e) {
System.out.println("ERR " + e.getMessage());
if (e.getCause() != null) {
System.out.println(" causa: " + e.getCause().getClass().getSimpleName());
}
}
}
OK 12/10/2026
OK 29/02/2024
ERR día no válido: 29 (el mes 2 de 2026 tiene 28 días)
ERR día no válido: 31 (el mes 4 de 2026 tiene 30 días)
ERR mes no válido: 13
ERR formato no válido: "1-1-2026" (usa dd/mm/aaaa)
ERR formato no válido: "aa/10/2026" (solo números)
causa: NumberFormatException
Las excepciones tienen un constructor IllegalArgumentException(String mensaje, Throwable causa). Pasándole la excepción original como segundo argumento, quien capture la nueva puede consultarla con getCause().
Nivel 3 — Desafío: un banco con su propia familia de excepciones
Enunciado. Diseña la jerarquía de errores de un banco y úsala en transferencias:
BancoException extends Exception(checked): la madre de todos los problemas del banco.CuentaNoEncontradaExceptionySaldoInsuficienteException, ambas hijas deBancoException. La segunda guarda cuánto falta (getFaltan()).Cuentacon IBAN y saldo.Bancocon un array de cuentas y contador:abrir(iban, saldo),buscar(iban)(lanzaCuentaNoEncontradaException) ytransferir(origen, destino, cantidad) throws BancoException.transferirdebe ser atómica: si algo falla, no se mueve ni un céntimo. Cantidades no positivas y transferencias a la misma cuenta se rechazan conIllegalArgumentException.- En el
main, si el problema es de saldo, sugiere la cantidad que sí se podría transferir. Al final, comprueba que el dinero total del banco no ha cambiado.
Pruebas y salida esperada:
Banco banco = new Banco(3);
banco.abrir("ES01", 500);
banco.abrir("ES02", 100);
banco.abrir("ES03", 0);
double antes = banco.total();
String[][] ordenes = {
{"ES01", "ES02", "200"},
{"ES02", "ES99", "50"},
{"ES02", "ES01", "400"},
{"ES02", "ES02", "10"},
{"ES01", "ES03", "-5"},
{"ES02", "ES03", "300"}
};
OK ES01 -> ES02 (200 €) ERR ES02 -> ES99 (50 €): no existe la cuenta ES99 ERR ES02 -> ES01 (400 €): saldo insuficiente en ES02: faltan 100,00 €. ¿Transferir 300,00 € en su lugar? ERR ES02 -> ES02 (10 €): origen y destino son la misma cuenta ERR ES01 -> ES03 (-5 €): cantidad no válida: -5.0 OK ES02 -> ES03 (300 €) Correctas: 2 de 6 ES01: 300,00 € ES02: 0,00 € ES03: 300,00 € Dinero total antes: 600,00 € · después: 600,00 €
Para que la transferencia sea atómica, haz todas las comprobaciones (buscar las dos cuentas, mirar el saldo) antes de tocar ningún saldo. Y en el main, el catch de SaldoInsuficienteException tiene que ir antes que el de BancoException.
¿Qué falla aquí?
Tres fragmentos que compilan. Antes de leer la explicación, intenta predecir qué imprimen.
Fragmento 1 — El try fuera de sitio
String[] notas = {"8", "6", "siete", "9", "5"};
int suma = 0;
try {
for (String n : notas) {
suma += Integer.parseInt(n);
}
} catch (NumberFormatException e) {
System.out.println("Nota ignorada: " + e.getMessage());
}
System.out.println("Suma: " + suma);
Imprime Nota ignorada: For input string: "siete" y Suma: 14, cuando las notas válidas suman 28. Es mi historia del principio. Al fallar "siete", se salta **el resto del try**, que incluye el resto del bucle: el 9 y el 5 nunca se suman. El mensaje dice «ignorada» en singular, pero en realidad se han ignorado tres. La solución es mover el try dentro del for:
for (String n : notas) {
try {
suma += Integer.parseInt(n);
} catch (NumberFormatException e) {
System.out.println("Nota ignorada: " + e.getMessage());
}
}
Pregúntate «si esto falla, ¿qué quiero que no se ejecute?». Eso, y solo eso, va dentro del try.
Fragmento 2 — El finally que cambia el valor (o no)
static int intentos() {
int x = 1;
try {
return x;
} finally {
x = 2;
}
}
System.out.println(intentos());
Imprime 1. El finally se ejecuta, sí, y x pasa a valer 2. Pero el valor que se devuelve ya se había calculado y guardado al llegar al return x, antes de entrar en el finally. Cambiar x después no cambia lo que ya está «en camino».
«arreglarlo» poniendo return 2; dentro del finally. Eso sí cambia el resultado, pero también se traga cualquier excepción que viniera del try: el return del finally gana siempre. Nunca pongas return en un finally; úsalo solo para limpiar.
Fragmento 3 — El catch que no hace nada
static double leerNota(String t) {
double nota = 0;
try {
nota = Double.parseDouble(t);
} catch (NumberFormatException e) {
}
return nota;
}
double media = (leerNota("8") + leerNota("6,5") + leerNota("9")) / 3;
System.out.printf("Media: %.2f%n", media);
Imprime Media: 5,67, cuando debería ser 7,83. "6,5" no se puede convertir (lleva coma), el catch vacío se traga el error y leerNota devuelve el 0 inicial como si fuera una nota. Nada en rojo, nada en la consola: un resultado falso que parece verdadero.
Hay dos soluciones razonables: aceptar la coma (t.replace(',', '.')) si es un formato válido para ti, o no capturar la excepción y dejar que suba hasta alguien que sepa qué hacer. Lo que nunca vale es un catch vacío.
Soluciones
Nivel 1 — Conversor seguro
public class Conversor {
// Intenta convertir; si no puede, devuelve el valor por defecto
static int aEntero(String texto, int porDefecto) {
try {
return Integer.parseInt(texto.trim());
} catch (NumberFormatException e) {
return porDefecto; // aquí SÍ es correcto no relanzar: es el contrato del método
}
}
// Simula pedir un número: recorre las entradas hasta encontrar una válida dentro del rango
static int pedirEntre(String[] entradas, int min, int max) {
for (int i = 0; i < entradas.length; i++) {
try {
int n = Integer.parseInt(entradas[i].trim());
if (n < min || n > max) {
System.out.println(" \"" + entradas[i] + "\": fuera de rango [" + min + ", " + max + "]");
continue; // siguiente intento
}
System.out.println(" Aceptado " + n + " en el intento " + (i + 1));
return n;
} catch (NumberFormatException e) {
System.out.println(" \"" + entradas[i] + "\": no es un número entero");
}
}
throw new IllegalStateException("ninguna entrada válida en " + entradas.length + " intentos");
}
public static void main(String[] args) {
System.out.println(aEntero("42", 0) + " " + aEntero(" 7 ", 0) + " " + aEntero("3.5", -1) + " " + aEntero("", -1));
System.out.println("Edad:");
int edad = pedirEntre(new String[]{"veinte", "-3", "150", " 19 "}, 0, 120);
System.out.println("Nota:");
try {
pedirEntre(new String[]{"diez", "11"}, 0, 10);
} catch (IllegalStateException e) {
System.out.println(" Error: " + e.getMessage());
}
System.out.println("Edad guardada: " + edad);
}
}
Por qué así:
- **
aEnterocaptura y devuelve un valor por defecto. ¿No dijimos que capturar y «seguir como si nada» era malo? Aquí es distinto: el método promete** en su nombre y en sus parámetros que, si no puede convertir, devolveráporDefecto. Quien lo llama lo sabe y lo eligió. El problema del fragmento 3 era que nadie sabía que el 0 significaba «error». - **
"3.5"devuelve -1:**Integer.parseIntno acepta decimales. Si quisieras redondear, tendrías que usarDouble.parseDoubley convertir. - **
trydentro delfor:** cada entrada tiene su oportunidad, la lección del fragmento 1. - **El rango se comprueba con
if, no con excepciones:**19y150son enteros perfectamente válidos para Java; que estén fuera de rango es tu regla.continuesalta al siguiente intento sin ejecutar elreturn. - **Si se agotan los intentos,
IllegalStateException:** el método no puede cumplir lo que promete (devolver un número válido), así que no puede devolver nada razonable. Lanzar es la única opción honesta, y es unchecked porque la situación indica que quien lo llama le pasó datos insuficientes.
Nivel 2 — Fecha validada
public class Fecha {
private final int dia;
private final int mes;
private final int anio;
public Fecha(int dia, int mes, int anio) {
if (mes < 1 || mes > 12) {
throw new IllegalArgumentException("mes no válido: " + mes);
}
int max = diasDelMes(mes, anio);
if (dia < 1 || dia > max) {
throw new IllegalArgumentException("día no válido: " + dia + " (el mes " + mes + " de " + anio + " tiene " + max + " días)");
}
this.dia = dia; // solo se guarda si todo es correcto
this.mes = mes;
this.anio = anio;
}
public static boolean esBisiesto(int anio) {
return (anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0;
}
public static int diasDelMes(int mes, int anio) {
switch (mes) {
case 2:
return esBisiesto(anio) ? 29 : 28;
case 4: case 6: case 9: case 11:
return 30;
default:
return 31;
}
}
// "12/10/2026" -> Fecha. Cualquier problema se convierte en IllegalArgumentException
public static Fecha parse(String texto) {
String[] partes = texto.trim().split("/");
if (partes.length != 3) {
throw new IllegalArgumentException("formato no válido: \"" + texto + "\" (usa dd/mm/aaaa)");
}
try {
int d = Integer.parseInt(partes[0]);
int m = Integer.parseInt(partes[1]);
int a = Integer.parseInt(partes[2]);
return new Fecha(d, m, a);
} catch (NumberFormatException e) {
// traducimos el error técnico a uno del dominio, y guardamos el original como causa
throw new IllegalArgumentException("formato no válido: \"" + texto + "\" (solo números)", e);
}
}
@Override
public String toString() {
return String.format("%02d/%02d/%04d", dia, mes, anio);
}
}
Por qué así:
- Validar en el constructor garantiza que no puede existir una
Fechainválida: si el constructor lanza, el objeto no llega a crearse. Es la versión «con excepciones» delsetNotade clases y objetos. - El mes se comprueba antes que el día porque para saber cuántos días tiene el mes, el mes tiene que ser válido. El orden de las comprobaciones sigue importando.
- **
parsetraduceNumberFormatExceptionaIllegalArgumentException.** A quien usaFechale da igual que por dentro se useparseInt; le importa que «la fecha no es válida». Así, un solocatch (IllegalArgumentException e)sirve para todos los casos. - La excepción original se guarda como causa (segundo argumento). No se pierde información: si alguien necesita el detalle técnico, ahí está con
getCause(). - **¿Y si
new Fecha(...)lanza dentro deltrydeparse?** Lanza unaIllegalArgumentException, que no es unaNumberFormatException(es su madre, no su hija). Elcatchno la captura, sube tal cual y llega almaincon su mensaje original («día no válido…»). Es justo lo que queremos.
2024 es bisiesto (divisible entre 4 y no entre 100) y 2026 no. Por eso el 29/02/2024 existe y el 29/02/2026 no. El año 2000 también fue bisiesto por la regla del 400; el 1900, no.
Nivel 3 — Banco con excepciones propias
// Madre de todos los problemas del banco: capturándola, se capturan todas sus hijas
public class BancoException extends Exception {
public BancoException(String mensaje) {
super(mensaje);
}
}
public class CuentaNoEncontradaException extends BancoException {
private final String iban;
public CuentaNoEncontradaException(String iban) {
super("no existe la cuenta " + iban);
this.iban = iban;
}
public String getIban() { return iban; }
}
public class SaldoInsuficienteException extends BancoException {
private final double faltan;
public SaldoInsuficienteException(String iban, double faltan) {
super(String.format("saldo insuficiente en %s: faltan %.2f €", iban, faltan));
this.faltan = faltan;
}
public double getFaltan() { return faltan; }
}
public class Cuenta {
private final String iban;
private double saldo;
public Cuenta(String iban, double saldo) {
this.iban = iban;
this.saldo = saldo;
}
public String getIban() { return iban; }
public double getSaldo() { return saldo; }
// Solo el banco las usa, y solo después de validarlo todo
void sacar(double cantidad) { saldo -= cantidad; }
void meter(double cantidad) { saldo += cantidad; }
}
public class Banco {
private final Cuenta[] cuentas;
private int numCuentas = 0;
public Banco(int capacidad) {
cuentas = new Cuenta[capacidad];
}
public void abrir(String iban, double saldoInicial) {
if (numCuentas == cuentas.length) {
throw new IllegalStateException("el banco está lleno");
}
cuentas[numCuentas++] = new Cuenta(iban, saldoInicial);
}
public Cuenta buscar(String iban) throws CuentaNoEncontradaException {
for (int i = 0; i < numCuentas; i++) {
if (cuentas[i].getIban().equals(iban)) {
return cuentas[i];
}
}
throw new CuentaNoEncontradaException(iban);
}
public void transferir(String origen, String destino, double cantidad) throws BancoException {
if (cantidad <= 0) {
throw new IllegalArgumentException("cantidad no válida: " + cantidad);
}
Cuenta o = buscar(origen); // buscamos las DOS antes de mover un céntimo
Cuenta d = buscar(destino);
if (o == d) {
throw new IllegalArgumentException("origen y destino son la misma cuenta");
}
if (cantidad > o.getSaldo()) {
throw new SaldoInsuficienteException(origen, cantidad - o.getSaldo());
}
o.sacar(cantidad); // a partir de aquí ya no puede fallar nada
d.meter(cantidad);
}
public double total() {
double t = 0;
for (int i = 0; i < numCuentas; i++) {
t += cuentas[i].getSaldo();
}
return t;
}
public void listar() {
for (int i = 0; i < numCuentas; i++) {
System.out.printf(" %s: %8.2f €%n", cuentas[i].getIban(), cuentas[i].getSaldo());
}
}
}
public class Main {
public static void main(String[] args) {
Banco banco = new Banco(3);
banco.abrir("ES01", 500);
banco.abrir("ES02", 100);
banco.abrir("ES03", 0);
double antes = banco.total();
String[][] ordenes = {
{"ES01", "ES02", "200"},
{"ES02", "ES99", "50"},
{"ES02", "ES01", "400"},
{"ES02", "ES02", "10"},
{"ES01", "ES03", "-5"},
{"ES02", "ES03", "300"}
};
int correctas = 0;
for (String[] o : ordenes) {
String texto = o[0] + " -> " + o[1] + " (" + o[2] + " €)";
try {
banco.transferir(o[0], o[1], Double.parseDouble(o[2]));
correctas++;
System.out.println("OK " + texto);
} catch (SaldoInsuficienteException e) {
System.out.printf("ERR %s: %s. ¿Transferir %.2f € en su lugar?%n",
texto, e.getMessage(), Double.parseDouble(o[2]) - e.getFaltan());
} catch (BancoException e) {
System.out.println("ERR " + texto + ": " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("ERR " + texto + ": " + e.getMessage());
}
}
System.out.println("\nCorrectas: " + correctas + " de " + ordenes.length);
banco.listar();
System.out.printf("Dinero total antes: %.2f € · después: %.2f €%n", antes, banco.total());
}
}
Por qué así:
- **Una madre común (
BancoException)** permite dos niveles de detalle.transferirdeclara solothrows BancoException, aunque lance dos tipos distintos; y en elmaintratamos a una hija de forma especial (el saldo, con su sugerencia) y al resto de la familia de golpe. Si mañana añadesCuentaBloqueadaException extends BancoException,transferirno cambia su firma y elmainya la captura. - **El
catchde la hija va antes que el de la madre.** SiBancoExceptionfuera primero, capturaría también el saldo insuficiente y nunca se mostraría la sugerencia (y Java no compilaría elcatchde abajo). - Atomicidad: todas las comprobaciones (cantidad, las dos cuentas, que no sean la misma, el saldo) van antes de
sacarymeter. Si se buscara el destino después de sacar el dinero del origen, la orden aES99haría desaparecer 50 €. La última línea de la salida lo demuestra: entraron 600 € y quedan 600 €. - **
sacarymetersin modificador** (visibilidad de paquete): las usa elBanco, pero no están pensadas para el resto del programa. Así nadie puede saltarse las validaciones detransferir. - **
getFaltan()se usa para calcular la sugerencia:** pedías 400, faltan 100, se pueden transferir 300. Es el dato útil que la excepción lleva dentro, como en el cajero de la práctica.
Declarar en transferir throws CuentaNoEncontradaException, SaldoInsuficienteException, ... con todas las hijas, una a una. Funciona, pero cada excepción nueva obliga a cambiar la firma y todos los sitios que la llaman. Declarar la madre es más limpio.
Míralo paso a paso en Python Tutor
Python Tutor ejecuta el código línea a línea y dibuja las variables y las llamadas. Vamos a ver en directo el fragmento 2, el del finally que «cambia» el valor. Elige Java, pega esto y avanza con Next:
public class Tutor {
static int intentos() {
int x = 1;
try {
return x;
} finally {
x = 2;
}
}
public static void main(String[] args) {
System.out.println(intentos());
}
}
Salida:
1
Qué observar:
- Al entrar en
intentos, aparece un marco conx = 1. - Al ejecutar
return x;, fíjate en que la ejecución no sale del método: salta alfinally. En ese momento el 1 ya está decidido como valor de retorno, aunque el método todavía no haya terminado. - Dentro del
finally,xcambia a 2 en el marco… pero el valor que se va a devolver sigue siendo 1. - Al salir,
mainrecibe ese 1. La variable cambió; lo que se devolvía, no. Ahora prueba a cambiarx = 2;porreturn 2;y mira cómo elfinally«pisa» el resultado.
Chuletario: excepciones en Java
Lo esencial en 30 segundos
- Cuando salta una excepción en un
try, **el resto deltryse salta**; Java entra en el primercatchque encaje y después sigue.finallyse ejecuta siempre. catchde lo más concreto a lo más general. Hija antes que madre.- Checked (
Exception,IOException): captúrala o declárala conthrows. Unchecked (RuntimeException): no obliga. throw new X("mensaje")lanza;throws Xavisa en la firma. Excepción propia:class MiException extends Exception+super(mensaje).- Nunca un
catchvacío: informa, corrige o vuelve a lanzar (si traduces, guarda la causa).
Sintaxis
| Quiero… | Código | Resultado |
|---|---|---|
| Intentar algo que puede fallar | try { … } catch (NumberFormatException e) { … } | el programa sigue |
| Varios tipos, tratamientos distintos | varios catch, del concreto al general | entra en el primero que encaje |
| Varios tipos, mismo tratamiento | catch (NumberFormatException | ArrayIndexOutOfBoundsException e) | multi-catch |
| Algo que se ejecute siempre | finally { … } | también con return o break |
| Ver qué ha pasado | e.getMessage() | el texto del error |
| Ver el tipo | e.getClass().getSimpleName() | "NumberFormatException" |
| Lanzar un error | throw new IllegalArgumentException("…") | corta el método |
| Avisar en la firma | void f() throws IOException | quien llame debe tratarla |
| Excepción propia checked | class X extends Exception { X(String m) { super(m); } } | obliga a tratarla |
| Excepción propia unchecked | class X extends RuntimeException | no obliga |
| Traducir guardando la causa | throw new IllegalArgumentException("…", e) | getCause() devuelve e |
| Cerrar un fichero solo | try (BufferedReader r = new BufferedReader(new FileReader(f))) { … } | se cierra al salir |
Plantillas de los ejercicios
Reintentar con el try dentro del bucle (Conversor):
for (String t : entradas) {
try {
int n = Integer.parseInt(t.trim());
if (n < min || n > max) continue;
return n;
} catch (NumberFormatException e) {
System.out.println("no es un número: " + t);
}
}
throw new IllegalStateException("ninguna entrada válida");
Edad → aceptado 19 en el intento 4
Traducir una excepción técnica (Fecha):
try {
return new Fecha(Integer.parseInt(p[0]), Integer.parseInt(p[1]), Integer.parseInt(p[2]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("formato no válido: " + texto, e);
}
"aa/10/2026" → formato no válido…, causa NumberFormatException
Familia de excepciones (Banco):
try {
banco.transferir(origen, destino, cantidad);
} catch (SaldoInsuficienteException e) { // la hija, primero
// usar e.getFaltan()
} catch (BancoException e) { // el resto de la familia
System.out.println(e.getMessage());
}
600 € antes → 600 € después
Errores que más se repiten
| Síntoma | Causa probable |
|---|---|
| El programa se para con una traza roja | Excepción sin capturar: busca la primera línea de tu código en la traza |
unreported exception X; must be caught or declared to be thrown | Excepción checked sin try/catch ni throws |
exception X has already been caught | Un catch general antes que uno concreto |
exception X is never thrown in body of corresponding try statement | Capturas una checked que el try no puede lanzar |
| Se procesa solo una parte de los datos | El try está fuera del bucle |
| Resultado raro sin ningún error en pantalla | Un catch vacío o que devuelve un valor «por si acaso» |
For input string: "7,5" | Decimal con coma: parseDouble solo acepta punto |
empty String | Has intentado convertir un texto vacío |
| Una operación fallida deja datos a medias | Modificas el estado antes de terminar de validar |
Antes de entregar, pregúntate
- ¿Cada
tryenvuelve solo lo que no debe ejecutarse si algo falla (y está dentro del bucle cuando toca)? - ¿Mis
catchvan de lo más concreto a lo más general, y ninguno está vacío? - ¿He elegido bien entre checked y unchecked, y mis excepciones propias llevan un mensaje claro y los datos útiles?
- ¿Valido todo antes de modificar nada, para que un error no deje los datos a medias?
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →
