Excepciones en Java — práctica con un importador de notas, un cajero y un lector de ficheros (y el «Error» que no decía nada)
La primera vez que usé try/catch en serio estaba tan contento de que mi importador de notas ya no se cayera que lo envolví todo en un único bloque:
try {
// ...todo el programa...
} catch (Exception e) {
System.out.println("Error");
}
Ya no salían trazas en rojo. Lo que salía era la palabra Error. Sin más. ¿Qué línea del fichero? ¿Qué campo? ¿Faltaba un dato o estaba mal escrito? Ni idea. Había cambiado un programa que se caía por uno que fallaba en silencio, que es peor. Desde entonces sigo una regla: cada catch captura un tipo concreto de problema y da un mensaje que diga dónde y por qué. Esa regla es la columna vertebral de los tres programas de hoy.
Si ya has leído la teoría de excepciones, este artículo es para aplicarla con tres programas completos: un importador de notas que no se cae con líneas mal escritas, un cajero automático con excepciones propias y un lector de ficheros con try-with-resources. Todos son de dificultad media y trabajan con datos que se parecen a los de verdad: notas con coma, campos que faltan, cantidades imposibles y ficheros que no existen. Cada uno trae el código comentado, la salida y las dudas que suelen surgir.
Tabla de Contenidos
Antes de empezar
Conviene tener claro:
- Que cuando salta una excepción dentro de un
try, **el resto deltryse salta** y Java busca el primercatchque encaje. - Que los
catchse ordenan de lo más concreto a lo más general (NumberFormatExceptionantes queIllegalArgumentException, que es su madre). - La diferencia entre
throw(lanzar) ythrows(avisar en la firma), y entre excepciones checked (obligan) y unchecked (no obligan). - Que una excepción propia se crea heredando de
Exceptiony pasándole el mensaje consuper(...).
Si alguno te suena dudoso, repasa la teoría antes de seguir.
El tercer programa crea y lee un fichero notas.txt. Se guarda en la carpeta desde la que ejecutas el programa (en IntelliJ, la raíz del proyecto). Si no lo encuentras, busca ahí.
Programa 1: importador de notas que no se cae
Vamos a procesar líneas con el formato nombre;asignatura;nota, como las que exportaría una hoja de cálculo. Los datos vienen como vienen: notas con coma decimal, líneas a las que les falta un campo, notas que no son números, notas imposibles y campos vacíos. El programa tiene que procesar todas las líneas, explicar exactamente qué le pasa a cada una que falle y calcular la media de las válidas.
La idea
Si leíste la práctica de Strings, recordarás que allí validábamos la nota a mano, carácter a carácter, porque todavía no sabíamos tratar la NumberFormatException. Ahora sí: dejamos que Double.parseDouble lo intente y, si no puede, capturamos su excepción.
Separamos el trabajo en dos partes:
- Un método
leerNota(linea)que devuelve la nota o lanza una excepción con el motivo. No imprime nada ni decide nada: solo avisa. - El
main, que recorre las líneas con untrydentro del bucle (para que una línea mala no detenga las demás), captura cada tipo de problema por separado y lleva la cuenta con unfinally.
Antes de programar, piensa en qué problemas puede tener una línea y quién debería detectarlo: el número de campos y el rango los compruebas tú (throw); que el texto no sea un número lo detecta parseDouble por ti.
El código
public class ImportadorNotas {
// Devuelve la nota de una línea "nombre;asignatura;nota" o lanza una excepción explicando el problema
static double leerNota(String linea) {
String[] campos = linea.split(";", -1);
if (campos.length != 3) {
throw new IllegalArgumentException("se esperaban 3 campos y hay " + campos.length);
}
String texto = campos[2].trim().replace(',', '.'); // aceptamos "7,5" además de "7.5"
double nota = Double.parseDouble(texto); // puede lanzar NumberFormatException
if (nota < 0 || nota > 10) {
throw new IllegalArgumentException("nota fuera de rango (" + nota + ")");
}
return nota;
}
public static void main(String[] args) {
String[] lineas = {
"Ana Pérez;FP2;8.5",
"Luis Gómez;FP2;7,5",
"Marta Ruiz;FP2",
"Pablo Sosa;FP2;9x",
"Elena Díaz;FP2;12",
"Sara Vega;FP2;",
"Iván Mora;FP2;10"
};
double suma = 0;
int validas = 0;
int procesadas = 0;
for (int i = 0; i < lineas.length; i++) {
try {
double nota = leerNota(lineas[i]);
suma += nota;
validas++;
System.out.printf("Línea %d: OK (%.2f)%n", i + 1, nota);
} catch (NumberFormatException e) {
// va ANTES que IllegalArgumentException: es su hija
System.out.println("Línea " + (i + 1) + ": la nota no es un número -> " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Línea " + (i + 1) + ": " + e.getMessage());
} finally {
procesadas++; // se cuenta pase lo que pase
}
}
System.out.printf("%nProcesadas: %d · válidas: %d · con error: %d%n", procesadas, validas, procesadas - validas);
if (validas > 0) {
System.out.printf("Media: %.2f%n", suma / validas);
}
}
}
Salida
Línea 1: OK (8,50) Línea 2: OK (7,50) Línea 3: se esperaban 3 campos y hay 2 Línea 4: la nota no es un número -> For input string: "9x" Línea 5: nota fuera de rango (12.0) Línea 6: la nota no es un número -> empty String Línea 7: OK (10,00) Procesadas: 7 · válidas: 3 · con error: 4 Media: 8,67
Compruébalo a mano: (8,5 + 7,5 + 10) / 3 = 8,666… ✔. Y fíjate en la línea 2: el "7,5" que en la teoría tumbaba el programa ahora entra sin problemas, porque cambiamos la coma por un punto antes de convertir. Las cuatro líneas malas tienen cada una su motivo, y ninguna ha impedido procesar la siguiente.
empty String es el mensaje que da parseDouble cuando recibe un texto vacío. Es la línea de Sara Vega, que acaba en ;. Gracias al split(";", -1) el campo vacío se conserva y el error correcto es «no es un número» en lugar de «faltan campos».
Preguntas que te puedes hacer
**¿Por qué el catch (NumberFormatException e) va antes que el de IllegalArgumentException?** Porque NumberFormatException es una IllegalArgumentException (es su hija). Si el general fuera primero, capturaría también los errores de formato y el de abajo nunca se ejecutaría. Java ni siquiera lo compilaría: exception NumberFormatException has already been caught.
**¿Por qué leerNota no tiene throws en la firma si lanza excepciones?** Porque las dos que puede lanzar, IllegalArgumentException y NumberFormatException, son unchecked (hijas de RuntimeException). Java no obliga a declararlas. Podrías escribir throws IllegalArgumentException como documentación, pero no es obligatorio.
**¿Para qué sirve aquí el finally? Para contar las líneas procesadas pase lo que pase**. Si pusieras procesadas++ al final del try, solo contaría las válidas (en las malas, el resto del try se salta); si lo pusieras en cada catch, tendrías que repetirlo. En el finally se escribe una vez y no falla nunca.
**¿Por qué leerNota lanza una excepción en vez de devolver, por ejemplo, -1 si algo va mal?** Porque -1 no explica qué ha fallado, y quien llama podría olvidarse de comprobarlo y sumar un -1 a la media. Una excepción lleva su mensaje y obliga a decidir qué hacer: si nadie la captura, el programa se para en lugar de dar un resultado falso.
¿Y si quiero el número de línea dentro del mensaje de la excepción? leerNota no sabe en qué línea está: solo recibe el texto. Lo correcto es lo que hace el main: quien conoce el contexto (el número de línea) lo añade al mensaje cuando la captura.
Hacer Double.parseDouble(campos[2]) antes de comprobar campos.length. Con la línea de Marta, campos[2] lanzaría una ArrayIndexOutOfBoundsException que no estamos capturando y el programa se pararía. El orden de las comprobaciones sigue importando, aunque ahora haya try/catch.
Pruébalo tú
- Añade una comprobación de que la asignatura sea
FP1,FP2oIC2(sin distinguir mayúsculas), con su propio mensaje. - Crea una
LineaInvalidaException(checked) con el número de línea como atributo, y haz quemainla lance envolviendo la excepción original.
Programa 2: un cajero automático con excepciones propias
Ahora un cajero. La cuenta tiene un PIN y un saldo, y el cajero recibe una lista de operaciones (PIN tecleado y cantidad). Hay que contemplar todo lo que puede salir mal en un cajero de verdad: PIN incorrecto, bloqueo tras tres fallos seguidos, cantidades que no son múltiplo de 10, texto donde debería haber un número y saldo insuficiente. Y cada operación, salga bien o mal, debe quedar registrada.
La idea
Aquí conviene distinguir dos clases de problemas:
- Problemas del negocio que el programa debe tratar sí o sí: no hay saldo, la cuenta se ha bloqueado. Para ellos creamos excepciones propias y checked (
SaldoInsuficienteException,CuentaBloqueadaException), así Java obliga a quien use la cuenta a tratarlas. - Datos mal introducidos (PIN incorrecto, cantidad imposible):
IllegalArgumentException, unchecked, como hicimos en el importador.
Y un detalle de diseño: la excepción de saldo lleva dentro el dato útil (cuánto falta), igual que la NotaInvalidaException de la teoría llevaba la nota.
Antes de programar, piensa qué tiene que pasar cuando la cuenta se bloquea: no tiene sentido seguir procesando operaciones. ¿Cómo sales del bucle desde un catch? ¿Se registra igualmente esa operación?
El código
public class SaldoInsuficienteException extends Exception {
private final double faltan;
public SaldoInsuficienteException(double faltan) {
super(String.format("Saldo insuficiente: faltan %.2f €", faltan));
this.faltan = faltan;
}
public double getFaltan() { return faltan; }
}
public class CuentaBloqueadaException extends Exception {
public CuentaBloqueadaException() {
super("Cuenta bloqueada: 3 PIN incorrectos seguidos");
}
}
public class Cuenta {
private static final int MAX_FALLOS = 3;
private final String pin;
private double saldo;
private int fallos = 0;
public Cuenta(String pin, double saldo) {
this.pin = pin;
this.saldo = saldo;
}
public void comprobarPin(String intento) throws CuentaBloqueadaException {
if (fallos >= MAX_FALLOS) {
throw new CuentaBloqueadaException(); // ya estaba bloqueada
}
if (!pin.equals(intento)) {
fallos++;
if (fallos == MAX_FALLOS) {
throw new CuentaBloqueadaException(); // este fallo la bloquea
}
throw new IllegalArgumentException("PIN incorrecto (intentos restantes: " + (MAX_FALLOS - fallos) + ")");
}
fallos = 0; // PIN correcto: se reinicia la cuenta de fallos
}
public void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad <= 0 || cantidad % 10 != 0) {
throw new IllegalArgumentException("cantidad no válida: " + cantidad + " (múltiplos de 10)");
}
if (cantidad > saldo) {
throw new SaldoInsuficienteException(cantidad - saldo);
}
saldo -= cantidad; // solo llega aquí si todo es correcto
}
public double getSaldo() { return saldo; }
}
public class Cajero {
public static void main(String[] args) {
Cuenta cuenta = new Cuenta("1234", 120);
// Cada operación: {PIN tecleado, cantidad tecleada}
String[][] operaciones = {
{"1234", "50"},
{"1234", "35"},
{"1234", "veinte"},
{"1234", "100"},
{"1111", "20"},
{"1234", "20"},
{"0000", "10"},
{"9999", "10"},
{"8888", "10"},
{"1234", "10"}
};
int ticket = 0;
for (String[] op : operaciones) {
ticket++;
try {
cuenta.comprobarPin(op[0]);
double cantidad = Double.parseDouble(op[1]);
cuenta.retirar(cantidad);
System.out.printf("#%d Retirados %s €. Saldo: %.2f €%n", ticket, op[1], cuenta.getSaldo());
} catch (SaldoInsuficienteException e) {
System.out.printf("#%d %s (pedías %s €)%n", ticket, e.getMessage(), op[1]);
} catch (CuentaBloqueadaException e) {
System.out.println("#" + ticket + " " + e.getMessage() + ". Retenemos la tarjeta.");
break; // salimos del bucle... pero antes, finally
} catch (NumberFormatException e) {
System.out.println("#" + ticket + " \"" + op[1] + "\" no es una cantidad");
} catch (IllegalArgumentException e) {
System.out.println("#" + ticket + " " + e.getMessage());
} finally {
System.out.println(" (operación " + ticket + " registrada)");
}
}
}
}
Salida
#1 Retirados 50 €. Saldo: 70,00 € (operación 1 registrada) #2 cantidad no válida: 35.0 (múltiplos de 10) (operación 2 registrada) #3 "veinte" no es una cantidad (operación 3 registrada) #4 Saldo insuficiente: faltan 30,00 € (pedías 100 €) (operación 4 registrada) #5 PIN incorrecto (intentos restantes: 2) (operación 5 registrada) #6 Retirados 20 €. Saldo: 50,00 € (operación 6 registrada) #7 PIN incorrecto (intentos restantes: 2) (operación 7 registrada) #8 PIN incorrecto (intentos restantes: 1) (operación 8 registrada) #9 Cuenta bloqueada: 3 PIN incorrectos seguidos. Retenemos la tarjeta. (operación 9 registrada)
Repásalo a mano: 120 − 50 = 70; los 100 € de la operación 4 no se pueden sacar porque faltan 100 − 70 = 30; tras la 6 quedan 70 − 20 = 50. Fíjate también en la operación 7: vuelve a decir «intentos restantes: 2» porque la operación 6, con el PIN bueno, reinició los fallos. Y la 10 no llega a procesarse: el break de la 9 sale del bucle, aunque antes se ejecuta su finally.
Preguntas que te puedes hacer
**¿Por qué SaldoInsuficienteException es checked y el PIN incorrecto es una IllegalArgumentException? Es una decisión de diseño. Quedarse sin saldo es una situación normal del negocio que cualquier** código que use Cuenta debe contemplar, así que obligamos con una checked. Un PIN mal tecleado lo tratamos como un dato de entrada incorrecto, igual que la cantidad, con la unchecked estándar. En los dos casos el mensaje explica qué ha pasado.
**¿Qué hace exactamente break dentro de un catch si hay finally?** Sale del bucle, pero el finally de esa vuelta se ejecuta igualmente antes de salir. Por eso la operación 9 aparece como registrada. Lo mismo pasaría con un return.
**¿Por qué el catch de NumberFormatException va antes que el de IllegalArgumentException?** Por la misma razón que en el importador: es su hija. Así «veinte» recibe un mensaje propio («no es una cantidad») en lugar del mensaje genérico de parseDouble.
**¿Por qué retirar hace saldo -= cantidad al final y no al principio? Porque si alguna comprobación lanza una excepción, el método se corta ahí y el saldo no cambia**. Si restaras primero y comprobaras después, una operación fallida podría dejar la cuenta con dinero de menos. Primero se valida todo; el cambio, al final.
**¿Para qué sirve getFaltan() si el mensaje ya lo dice? Para que el código pueda usar** el dato, no solo imprimirlo: por ejemplo, para ofrecer «¿quiere retirar 70 € en su lugar?». El mensaje es para personas; los atributos de la excepción son para el programa.
Capturar CuentaBloqueadaException y seguir con el bucle como si nada. El programa no se cae, pero sigue pidiendo PIN a una cuenta bloqueada. Capturar una excepción no es solo «que no salga en rojo»: es decidir qué hacer, y aquí lo correcto es parar.
Cuando crees una excepción propia, piensa qué datos necesitaría quien la capture para actuar (cuánto falta, qué nota era, en qué línea) y guárdalos como atributos con su getter.
Pruébalo tú
- Añade un límite diario de 300 €: si una retirada lo supera, lanza una
LimiteDiarioExceptioncon la cantidad que aún se podría retirar hoy. - Cambia el diseño para que
CuentaBloqueadaExceptionherede deRuntimeException. ¿Qué deja de pedirte el compilador? ¿Te parece mejor o peor?
Programa 3: lector de notas desde un fichero
El último programa trabaja con un fichero de verdad. Primero crea notas.txt con algunas líneas (una de ellas mal escrita y otra incompleta) y después lo lee para calcular la media. Además intenta leer un segundo fichero que no existe. El programa tiene que distinguir entre un problema en una línea (se ignora esa línea y se sigue) y un problema con el fichero entero (no se puede calcular nada).
La idea
Tenemos dos niveles de error, y cada uno se trata en un sitio distinto:
- Errores de una línea (nota que no es un número, línea sin nota): se capturan dentro del bucle de lectura, con un
try/catchinterno, para que la lectura continúe. - Errores del fichero (no existe, no se puede leer): son
IOException, checked. El métodomediano sabe qué hacer con ellos, así que los declara conthrowsy deja que elmaindecida.
Para abrir el fichero usamos try-with-resources: Java lo cierra solo al salir del bloque, tanto si todo va bien como si salta una excepción.
Antes de programar, pregúntate en qué punto exacto puede fallar cada cosa: abrir el fichero (new FileReader), leer una línea (readLine), partirla (split y campos[1]) y convertirla (parseDouble). Cada fallo pertenece a uno de los dos niveles.
El código
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class LectorNotas {
// Crea el fichero de ejemplo (en un programa real ya existiría)
static void crearFichero(String ruta) throws IOException {
try (PrintWriter salida = new PrintWriter(new FileWriter(ruta))) {
salida.println("Ana;8.5");
salida.println("Luis;7,5");
salida.println("Marta;nueve");
salida.println("Pablo");
salida.println("Sara;6");
} // aquí se cierra solo
}
// Lee el fichero y devuelve la media. Los errores de UNA línea se tratan aquí;
// los errores del FICHERO (no existe, no se puede leer) se dejan subir con throws.
static double media(String ruta) throws IOException {
double suma = 0;
int validas = 0;
int numLinea = 0;
try (BufferedReader lector = new BufferedReader(new FileReader(ruta))) {
String linea;
while ((linea = lector.readLine()) != null) { // null = fin del fichero
numLinea++;
try {
String[] campos = linea.split(";");
double nota = Double.parseDouble(campos[1].trim().replace(',', '.'));
suma += nota;
validas++;
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
System.out.println(" Línea " + numLinea + " ignorada (" + linea + "): "
+ e.getClass().getSimpleName());
}
}
}
if (validas == 0) {
throw new IOException("no hay ninguna nota válida en " + ruta);
}
return suma / validas;
}
public static void main(String[] args) {
try {
crearFichero("notas.txt");
} catch (IOException e) {
System.out.println("No se pudo crear el fichero: " + e.getMessage());
return;
}
String[] rutas = {"notas.txt", "notas-2025.txt"};
for (String ruta : rutas) {
System.out.println("Leyendo " + ruta + "...");
try {
System.out.printf(" Media: %.2f%n", media(ruta));
} catch (IOException e) {
System.out.println(" No se pudo leer: " + e.getMessage());
}
}
}
}
Salida
Leyendo notas.txt... Línea 3 ignorada (Marta;nueve): NumberFormatException Línea 4 ignorada (Pablo): ArrayIndexOutOfBoundsException Media: 7,33 Leyendo notas-2025.txt... No se pudo leer: notas-2025.txt (No such file or directory)
Compruébalo: las notas válidas son 8,5, 7,5 y 6, y (8,5 + 7,5 + 6) / 3 = 7,33 ✔. Las dos líneas malas se saltan sin detener la lectura, y el fichero que no existe no tumba el programa: el main lo captura y pasa al siguiente.
El mensaje de «no existe» lo escribe el sistema operativo. En Windows en español verás algo como notas-2025.txt (El sistema no puede encontrar el archivo especificado). El tipo de excepción es el mismo: FileNotFoundException.
Preguntas que te puedes hacer
**¿Por qué hay un try dentro de otro?** Porque son dos niveles de error. El try exterior (el de recursos) se encarga del fichero: lo abre y lo cierra. El interior se encarga de cada línea: si una falla, se ignora y el while sigue. Si solo hubiera el exterior, la primera línea mala detendría toda la lectura.
**¿Por qué media declara throws IOException en vez de capturarla?** Porque media no sabe qué hacer si el fichero no existe: ¿avisar al usuario?, ¿probar con otro?, ¿usar una media por defecto? Eso lo decide quien la llama. Declarándola, el compilador obliga al main a tratarla (es checked), y así no se nos puede olvidar.
¿Cuándo se cierra el fichero? En cuanto se sale del bloque try (...), pase lo que pase: al terminar el while, o si saltara una IOException a mitad de lectura. No hay que escribir ningún close(). Las excepciones de las líneas no llegan a salir de ese bloque, porque las captura el try interno.
**¿Qué hace (linea = lector.readLine()) != null?** Lee la siguiente línea, la guarda en linea y comprueba si es null. readLine() devuelve null cuando ya no quedan líneas: es la forma clásica de recorrer un fichero entero.
**¿Por qué lanzamos nosotros una IOException si no hay ninguna nota válida?** Porque sin notas no se puede calcular una media (dividiríamos entre 0). Lanzar una excepción con un mensaje claro es mejor que devolver NaN o 0, que podrían pasar por un resultado real.
Abrir el fichero con new FileReader(...) fuera de un try-with-resources y cerrarlo al final del try normal. Si salta una excepción antes del close(), el fichero se queda abierto. Con try (...) eso no puede pasar.
Pruébalo tú
- Haz que el programa escriba un segundo fichero,
errores.txt, con las líneas ignoradas y el motivo de cada una. - Cambia el
catchmúltiple por doscatchseparados con mensajes distintos: «nota no numérica» y «falta la nota».
Errores típicos en esta práctica
- **Un único
catch (Exception e)para todo,** con un mensaje genérico: no sabrás qué ha fallado ni dónde. Captura tipos concretos y explica el motivo. - **Poner el
tryfuera del bucle:** el primer dato malo detiene todo el procesamiento. - **Ordenar mal los
catch:** el más concreto (NumberFormatException) va antes que el general (IllegalArgumentException). - Modificar el estado antes de validar: si
retirarresta y luego comprueba, una operación fallida deja la cuenta mal. Primero valida, después cambia. - Capturar una excepción y no hacer nada útil con ella: seguir operando con una cuenta bloqueada, o un
catchvacío que esconde el error. - **Abrir ficheros sin
try-with-resources:** si algo falla, el fichero puede quedarse abierto.
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. Elige Java, pega este cajero reducido y avanza con Next:
public class Tutor {
static class SaldoInsuficienteException extends Exception {
SaldoInsuficienteException(String mensaje) { super(mensaje); }
}
static double saldo = 100;
static void retirar(double cantidad) throws SaldoInsuficienteException {
if (cantidad > saldo) {
throw new SaldoInsuficienteException("Faltan " + (cantidad - saldo) + " €");
}
saldo -= cantidad;
}
public static void main(String[] args) {
double[] retiradas = {30, 90, 50};
for (double r : retiradas) {
try {
retirar(r);
System.out.println("OK, quedan " + saldo);
} catch (SaldoInsuficienteException e) {
System.out.println(e.getMessage());
} finally {
System.out.println("Ticket impreso");
}
}
}
}
Salida:
OK, quedan 70.0 Ticket impreso Faltan 20.0 € Ticket impreso OK, quedan 20.0 Ticket impreso
Qué observar:
- En la primera vuelta,
retirar(30)entra, resta y vuelve:saldopasa de 100 a 70 y la ejecución sigue con el «OK». - En la segunda, con 90, mira el
throw: se crea un objetoSaldoInsuficienteException(aparece en el montón con su mensaje) y el marco deretirardesaparece sin llegar asaldo -= cantidad. Por eso el saldo sigue en 70. - La ejecución salta directamente al
catchdemain, sin pasar por elprintlnde «OK». - En las tres vueltas, haya excepción o no, la ejecución pasa por el
finally. Cuenta los «Ticket impreso»: siempre hay uno por vuelta.
Resumen
- Captura tipos concretos de excepción y da mensajes que digan dónde y por qué ha fallado; un
catch (Exception e)genérico esconde los problemas. - El
tryva dentro del bucle cuando cada elemento debe tener su oportunidad. - Ordena los
catchde lo más concreto a lo más general; Java no compila el orden contrario. finallyse ejecuta siempre, incluso conbreakoreturn: úsalo para contar, registrar o cerrar.- Usa excepciones propias y checked para situaciones del negocio que quien llame debe tratar, y guarda en ellas los datos útiles.
- Abre los ficheros con
try-with-resourcesy separa los errores de una línea (se ignoran y se sigue) de los del fichero (se propagan conthrows).
Checklist: ¿lo has practicado?
- ¿Has ejecutado los tres programas y comprobado las salidas?
- ¿Sabes explicar por qué
NumberFormatExceptionse captura antes queIllegalArgumentException? - ¿Sabes qué pasa con el
finallycuando hay unbreaken elcatch? - ¿Entiendes por qué
retirarmodifica el saldo al final y no al principio? - ¿Sabes distinguir qué errores trata
mediay cuáles deja subir conthrows? - ¿Has intentado los «Pruébalo tú» sin mirar la teoría?
Si has respondido que sí a casi todo, estás listo para los ejercicios de excepciones, donde ya no te doy el código hecho. ¡Nos vemos allí!
Siguiente paso: ejerciciosExcepciones en Java — ejercicios con un conversor, fechas y un banco (y el try que puse en el sitio equivocado)¿Te ha servido este artículo? Así puedes apoyar SergioLearns →
