Control de flujo en Java — ejercicios con la nota de acceso, la biblioteca y un cajero de tres intentos (y el 9,5 que salía «Suficiente»)
Un compañero de clase me enseñó una vez su validador de notas, muy orgulloso: «funciona, lo he probado». Lo había probado con un 6. Le pedí que metiera un 9,5 y el programa contestó, sin despeinarse: «Suficiente». Ni error, ni excepción, ni aviso del compilador. Las condiciones estaban bien escritas, una por una; lo que estaba mal era el orden: la primera era nota >= 5, y un 9,5 también es mayor o igual que 5. En una cadena de else if la primera condición verdadera gana y las demás ni se miran. Ese fallo lo vas a ver con su salida real en «¿Qué falla aquí?», y el primer ejercicio te obliga a ordenarlas bien.
Aquí tienes tres ejercicios de control de flujo en Java, de menos a más: la nota de acceso a la universidad (rangos con else if y validar antes de calcular), el préstamo de la biblioteca de la facultad (switch con String y cada caso con sus llaves) y un cajero con tres intentos para el PIN sin bucles y una operación con switch. Como en la práctica de control de flujo, no hay ni bucles ni arrays: cada ejecución hace una sola cosa. Al final tienes el cuestionario tipo examen y el chuletario.
Tabla de Contenidos
Cómo usar este artículo
- Lee el enunciado y la salida esperada de cada ejercicio. La salida es real: si tu programa imprime lo mismo con los mismos datos, vas bien.
- Inténtalo sin mirar nada al menos 15 minutos. Si te atascas, abre la pista.
- Antes de las soluciones, haz «¿Qué falla aquí?»: tres fragmentos con un error cada uno. Predice qué imprimen antes de leer la explicación.
- Compara tu solución con la comentada. No tiene que ser igual: tiene que dar la misma salida con los mismos datos y por las mismas razones.
Las salidas son de una única ejecución con Java 21 en un equipo en español, así que los decimales se teclean y se imprimen con coma (11,8). Lo que aparece detrás de cada pregunta es lo que se tecleó. En el entorno de Java de la web lo tecleado va en la pestaña Entrada, una línea por dato.
Nivel básico
Ejercicio 1 — Validador de nota de acceso
Escribe ValidadorNota: pide una nota de acceso a la universidad (de 0 a 14, como en la EBAU con la fase específica) y la clasifica así:
| Nota | Clasificación |
|---|---|
| menos de 5 | No apto |
| de 5 a menos de 7 | Suficiente |
| de 7 a menos de 9 | Notable |
| de 9 a menos de 11 | Muy bueno |
| 11 o más | Excelente |
Después dice cuánto le sobra o le falta para la nota de corte de GCID, que tomaremos como 10,5 (inventada), con dos decimales. Si la nota está fuera de 0-14, avisa y no calcula nada más. Por último imprime el margen sin redondear con println, para ver qué guarda de verdad el double.
Salida esperada con 11,8:
=== VALIDADOR DE NOTA DE ACCESO === Nota de acceso (0-14): 11,8 --- Resultado --- Clasificación: Excelente Entras en GCID: 1,30 por encima del corte (10,5). Margen sin redondear: 1.3000000000000007
Con 15:
=== VALIDADOR DE NOTA DE ACCESO === Nota de acceso (0-14): 15 Dato no válido: la nota de acceso va de 0 a 14.
Con 4,99:
=== VALIDADOR DE NOTA DE ACCESO === Nota de acceso (0-14): 4,99 --- Resultado --- Clasificación: No apto Te faltan 5,51 para el corte (10,5). Margen sin redondear: -5.51
Valida el rango con un if al principio y corta con return; si falla. Para la clasificación, una cadena else if que empiece por el tramo más alto (nota >= 11): así cada condición solo necesita el límite de abajo. El margen es nota - CORTE: si es negativo, imprime -margen en el mensaje de lo que falta. ¿Por qué 1,30 con printf y otra cosa con println? Lo verás en la solución.
Nivel intermedio
Ejercicio 2 — Menú de biblioteca
Escribe Biblioteca: la biblioteca de la facultad atiende una operación por ejecución, elegida en un menú.
- 1. Pedir un préstamo: pide el tipo de socio, cuántos libros tiene ya y el título. Según el tipo: estudiante 15 días y 3 libros como mucho; PDI 30 días y 6 libros; visitante 7 días y 1 libro. Si el tipo no existe, si falta el título o si ya tiene el máximo, lo dice; si no, confirma el préstamo.
- 2. Devolver un libro: pide el tipo de socio y los días de retraso. A tiempo, lo agradece; el PDI no paga multa; los demás pagan 0,20 € por día con un máximo de 5 €.
- 3. Ver las normas.
- Cualquier otro número: «Opción no válida».
El tipo de socio se acepta con cualquier combinación de mayúsculas (PDI, pdi). Las normas son inventadas.
Salida esperada de un préstamo y de un préstamo que no cabe:
=== BIBLIOTECA DE LA FACULTAD === 1. Pedir un préstamo 2. Devolver un libro 3. Ver las normas Opción: 1 Tipo de socio (estudiante/pdi/visitante): estudiante Libros que ya tienes: 1 Título: Clean Code Préstamo hecho: «Clean Code» durante 15 días (2 de 3 libros).
=== BIBLIOTECA DE LA FACULTAD === 1. Pedir un préstamo 2. Devolver un libro 3. Ver las normas Opción: 1 Tipo de socio (estudiante/pdi/visitante): visitante Libros que ya tienes: 1 Título: Java a fondo No puedes llevarte más: ya tienes 1 de 1.
Devoluciones con 9 días de retraso, con 40 (llega al máximo) y de un PDI escrito en mayúsculas:
=== BIBLIOTECA DE LA FACULTAD === 1. Pedir un préstamo 2. Devolver un libro 3. Ver las normas Opción: 2 Tipo de socio (estudiante/pdi/visitante): estudiante Días de retraso: 9 Multa: 1,80 € (0,20 € por día, como mucho 5 €).
=== BIBLIOTECA DE LA FACULTAD === 1. Pedir un préstamo 2. Devolver un libro 3. Ver las normas Opción: 2 Tipo de socio (estudiante/pdi/visitante): estudiante Días de retraso: 40 Multa: 5,00 € (0,20 € por día, como mucho 5 €).
=== BIBLIOTECA DE LA FACULTAD === 1. Pedir un préstamo 2. Devolver un libro 3. Ver las normas Opción: 2 Tipo de socio (estudiante/pdi/visitante): PDI Días de retraso: 12 Devuelto con 12 días de retraso: el PDI no paga multa.
Y una opción que no existe:
=== BIBLIOTECA DE LA FACULTAD === 1. Pedir un préstamo 2. Devolver un libro 3. Ver las normas Opción: 5 Opción no válida: 5
El menú es un switch (opcion). Dentro del caso 1, otro switch (tipo) con String que dé valor a dias y maximo (con su break en cada caso, o con flechas). Si en los casos 1 y 2 declaras una variable con el mismo nombre (tipo), pon cada caso entre llaves propias: case 1: { … break; }. Lee el tipo con trim().toLowerCase() para no depender de las mayúsculas. La multa: retraso * 0.20 y, si pasa de 5, se queda en 5.
Desafío final
Ejercicio 3 — Cajero automático con PIN
Escribe CajeroPIN: el PIN correcto es 4321 y hay tres intentos sin usar bucles. Tras cada fallo dice cuántos intentos quedan; al tercer fallo, «Tarjeta bloqueada. Llama a tu banco.» y el programa termina. Con el PIN bien, un menú con una operación:
- 1. Consultar saldo (empieza en 500,00 €).
- 2. Sacar dinero: la cantidad tiene que ser positiva y múltiplo de 10, como mucho 600 € por operación, y no más que el saldo. Comprueba en ese orden.
- 3. Ingresar dinero: de 1 a 1000 € por ingreso.
Lee siempre líneas (nextLine() + Integer.parseInt), y guarda el PIN como texto. Salida esperada acertando a la segunda y sacando 120 €:
=== CAJERO AUTOMÁTICO === PIN: 1111 PIN incorrecto (2 intentos restantes) PIN: 4321 PIN correcto. 1. Consultar saldo 2. Sacar dinero 3. Ingresar dinero Operación: 2 Cantidad (múltiplo de 10): 120 Retira 120 €. Saldo: 380,00 €
Tres fallos:
=== CAJERO AUTOMÁTICO === PIN: 1111 PIN incorrecto (2 intentos restantes) PIN: 2222 PIN incorrecto (1 intento restante) PIN: 0000 Tarjeta bloqueada. Llama a tu banco.
Sin saldo suficiente, con una cantidad que no se puede dar en billetes y con un ingreso:
=== CAJERO AUTOMÁTICO === PIN: 4321 PIN correcto. 1. Consultar saldo 2. Sacar dinero 3. Ingresar dinero Operación: 2 Cantidad (múltiplo de 10): 550 Saldo insuficiente: tienes 500,00 €.
=== CAJERO AUTOMÁTICO === PIN: 4321 PIN correcto. 1. Consultar saldo 2. Sacar dinero 3. Ingresar dinero Operación: 2 Cantidad (múltiplo de 10): 125 Cantidad no válida: el cajero solo da billetes de 10, 20 y 50.
=== CAJERO AUTOMÁTICO === PIN: 4321 PIN correcto. 1. Consultar saldo 2. Sacar dinero 3. Ingresar dinero Operación: 3 Cantidad a ingresar: 200 Ingreso hecho. Saldo: 700,00 €
Sin bucles, los tres intentos son tres if (!pin.equals(PIN)), cada uno dentro del anterior: solo se pide el segundo PIN si el primero falló. En el más profundo, bloqueo y return;. Si llegas a la línea de después del primer if, es que algún intento acertó. Para el plural, intentos vale 2 tras el primer fallo y 1 tras el segundo. ¿Por qué "4321" y no 4321? Piensa en un PIN como 0420.
¿Qué falla aquí?
Antes de las soluciones, tres fragmentos con un error cada uno: los tres compilan sin avisos. Lee cada uno y predice qué imprime antes de mirar la salida.
Error 1
public class OrdenElseIf {
public static void main(String[] args) {
double nota = 9.5;
String clase;
if (nota >= 5) {
clase = "Suficiente";
} else if (nota >= 7) {
clase = "Notable";
} else if (nota >= 9) {
clase = "Muy bueno";
} else {
clase = "No apto";
}
System.out.println(nota + " → " + clase);
}
}
¿Qué imprime con nota = 9.5? Salida real:
9.5 → Suficiente
9.5 >= 5 es verdadero, así que entra en la primera rama y la cadena termina ahí: los else if de después no se evalúan. El dibujo lo enseña sobre la recta de notas:
Con nota >= 5 arriba, todo lo que va de 5 a 14 se queda en «Suficiente»: las ramas de «Notable» y «Muy bueno» son código muerto.
Ordenar los rangos de menor a mayor usando solo el límite de abajo. Cada condición es correcta por separado, pero la primera se lo come todo. Fíjate en el 9.5 de la salida: lo imprime + con un double, que usa siempre punto.
Arreglo: de mayor a menor.
public class OrdenElseIf {
public static void main(String[] args) {
double nota = 9.5;
String clase;
if (nota >= 9) { // de mayor a menor
clase = "Muy bueno";
} else if (nota >= 7) {
clase = "Notable";
} else if (nota >= 5) {
clase = "Suficiente";
} else {
clase = "No apto";
}
System.out.println(nota + " → " + clase);
}
}
9.5 → Muy bueno
Si prefieres ir de menor a mayor, cada condición necesita sus dos límites: nota >= 5 && nota < 7. Funciona, pero es más largo y si te equivocas en un límite aparecen huecos (¿qué pasa con un 6,99 si escribes nota <= 6.9?). De mayor a menor no hay huecos posibles.
Error 2
public class DiasPrestamo {
public static void main(String[] args) {
String tipo = "estudiante";
int dias = 0;
switch (tipo) {
case "estudiante":
dias = 15;
case "pdi":
dias = 30;
case "visitante":
dias = 7;
}
System.out.println(tipo + ": " + dias + " días");
}
}
¿Cuántos días le da a un estudiante? Salida real:
estudiante: 7 días
Falta el break en cada caso. Java entra en case "estudiante", pone dias = 15, cae en case "pdi" y lo cambia a 30, cae en case "visitante" y lo deja en 7. El último valor es el que se queda. Es el mismo fall-through que en la práctica sumaba plazos a propósito; aquí destroza el resultado.
Pensar que el switch sale solo al terminar un case. En el switch clásico solo sale con break (o al llegar al final). Sin él, todos los casos de debajo se ejecutan.
Arreglo: el switch con flechas, que además devuelve el valor.
public class DiasPrestamo {
public static void main(String[] args) {
String tipo = "estudiante";
int dias = switch (tipo) { // con flechas no hay fall-through
case "estudiante" -> 15;
case "pdi" -> 30;
case "visitante" -> 7;
default -> 0; // en un switch que devuelve valor, default es obligatorio
};
System.out.println(tipo + ": " + dias + " días");
}
}
estudiante: 15 días
Error 3
public class EdadValida {
public static void main(String[] args) {
int edad = 150;
if (edad < 16 && edad > 99) {
System.out.println("Edad fuera de rango");
} else {
System.out.println("Edad correcta: " + edad);
}
}
}
¿Rechaza los 150 años? Salida real:
Edad correcta: 150
edad < 16 && edad > 99 pide que la edad sea a la vez menor que 16 y mayor que 99: ningún número cumple las dos cosas, así que la condición es siempre falsa y todo cae en el else. Con 150: 150 < 16 es false, y con && Java ni siquiera mira la segunda parte (cortocircuito).
Traducir «fuera de rango» con &&. «Fuera» significa «por debajo o por encima»: ||. Si lo piensas en positivo («dentro» = edad >= 16 && edad <= 99), lo de fuera es su negación: !(edad >= 16 && edad <= 99), que por las leyes de De Morgan es edad < 16 || edad > 99.
Arreglo:
public class EdadValida {
public static void main(String[] args) {
int edad = 150;
if (edad < 16 || edad > 99) { // basta con que falle UNA de las dos
System.out.println("Edad fuera de rango");
} else {
System.out.println("Edad correcta: " + edad);
}
}
}
Edad fuera de rango
Soluciones comentadas
Solución Ejercicio 1
import java.util.Scanner;
/**
* Nota de acceso a la universidad (0-14): validar el rango ANTES de calcular y clasificar
* con else if de mayor a menor. Nota de corte inventada.
*/
public class ValidadorNota {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
final double CORTE = 10.5; // nota de corte de GCID (inventada)
System.out.println("=== VALIDADOR DE NOTA DE ACCESO ===");
System.out.print("Nota de acceso (0-14): ");
double nota = sc.nextDouble(); // equipo en español: se teclea con coma (11,8)
// 1. Validar primero: si el dato no tiene sentido, se corta aquí y no se calcula nada más
if (nota < 0 || nota > 14) {
System.out.println("Dato no válido: la nota de acceso va de 0 a 14.");
return;
}
// 2. Clasificar de MAYOR a menor: la primera condición verdadera gana y las demás ni se miran.
// Por eso basta con «nota >= 9» y no hace falta «nota >= 9 && nota < 11».
String clase;
if (nota >= 11) {
clase = "Excelente";
} else if (nota >= 9) {
clase = "Muy bueno";
} else if (nota >= 7) {
clase = "Notable";
} else if (nota >= 5) {
clase = "Suficiente";
} else {
clase = "No apto";
}
System.out.println();
System.out.println("--- Resultado ---");
System.out.println("Clasificación: " + clase);
// 3. Margen sobre el corte: positivo si entra, negativo si le falta
double margen = nota - CORTE;
if (margen >= 0) {
System.out.printf("Entras en GCID: %.2f por encima del corte (%.1f).%n", margen, CORTE);
} else {
System.out.printf("Te faltan %.2f para el corte (%.1f).%n", -margen, CORTE);
}
// println no usa el idioma del equipo: punto decimal y todos los decimales que tenga el double
System.out.println("Margen sin redondear: " + margen);
sc.close();
}
}
Por qué está hecho así:
- Validar primero y cortar con
return. Si el 15 siguiera adelante, saldría «Excelente» con 4,50 de margen: un resultado que parece correcto con un dato imposible. Con elreturnal principio, todo lo que viene después puede dar por hecho que la nota está entre 0 y 14. - De mayor a menor. Con 11,8 la primera condición (
11.8 >= 11) ya es verdadera: «Excelente». Con 4,99 fallan las cuatro y llega alelse: «No apto». Ningún valor entre 0 y 14 se queda sin clase. String clase;sin valor inicial. Java comprueba que todas las ramas le dan un valor antes de usarla. Si quitas elelsefinal,javacse queja:variable clase might not have been initialized. Es una ayuda: te avisa de que hay notas sin clasificar.- El margen con
printfy conprintln. 11,8 − 10,5 debería ser 1,3, pero en binario 11,8 no es exacto y eldoubleguarda 1.3000000000000007.printf("%.2f")redondea a dos decimales y usa la coma del equipo (1,30);printlnenseña el número tal cual, con punto, porque no tiene en cuenta el idioma. Con 4,99 la cuenta da −5,51 y aquí sí sale limpia. Por eso, para enseñar resultados,printf. -margenen el mensaje de lo que falta. «Te faltan -5,51» se lee raro; con el signo cambiado, «Te faltan 5,51».
| Nota tecleada | ¿Rango? | Primera condición verdadera | Clase | Margen |
|---|---|---|---|---|
| 11,8 | Sí | nota >= 11 | Excelente | 11,8 − 10,5 = 1,30 |
| 15 | No: 15 > 14 | (no llega) | (no llega) | (no llega) |
| 4,99 | Sí | ninguna: else | No apto | 4,99 − 10,5 = −5,51 |
Solución Ejercicio 2
import java.util.Scanner;
/**
* Biblioteca de la facultad: una operación por ejecución (préstamo, devolución o normas), sin arrays.
* Plazos, límites y multas inventados.
*/
public class Biblioteca {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== BIBLIOTECA DE LA FACULTAD ===");
System.out.println("1. Pedir un préstamo");
System.out.println("2. Devolver un libro");
System.out.println("3. Ver las normas");
System.out.print("Opción: ");
int opcion = sc.nextInt();
sc.nextLine(); // fuera el Intro antes de leer textos
switch (opcion) {
case 1: { // llaves propias: «tipo» también se declara en el caso 2
System.out.print("Tipo de socio (estudiante/pdi/visitante): ");
String tipo = sc.nextLine().trim().toLowerCase();
System.out.print("Libros que ya tienes: ");
int tiene = sc.nextInt();
sc.nextLine();
System.out.print("Título: ");
String titulo = sc.nextLine().trim();
int dias; // días de préstamo
int maximo; // libros a la vez
switch (tipo) { // switch con String: compara con equals(), no con ==
case "estudiante":
dias = 15;
maximo = 3;
break;
case "pdi":
dias = 30;
maximo = 6;
break;
case "visitante":
dias = 7;
maximo = 1;
break;
default:
dias = 0; // marca de «tipo desconocido»
maximo = 0;
}
if (dias == 0) {
System.out.println("Tipo de socio desconocido: " + tipo);
} else if (titulo.isEmpty()) {
System.out.println("Falta el título.");
} else if (tiene >= maximo) {
System.out.println("No puedes llevarte más: ya tienes " + tiene + " de " + maximo + ".");
} else {
System.out.printf("Préstamo hecho: «%s» durante %d días (%d de %d libros).%n",
titulo, dias, tiene + 1, maximo);
}
break;
}
case 2: {
System.out.print("Tipo de socio (estudiante/pdi/visitante): ");
String tipo = sc.nextLine().trim().toLowerCase();
System.out.print("Días de retraso: ");
int retraso = sc.nextInt();
if (retraso < 0) {
System.out.println("Dato no válido.");
} else if (retraso == 0) {
System.out.println("Devuelto a tiempo. Gracias.");
} else if (tipo.equals("pdi")) {
System.out.println("Devuelto con " + retraso + " días de retraso: el PDI no paga multa.");
} else {
double multa = retraso * 0.20; // 20 céntimos por día...
if (multa > 5) {
multa = 5; // ...con un máximo de 5 €
}
System.out.printf("Multa: %.2f € (0,20 € por día, como mucho 5 €).%n", multa);
}
break;
}
case 3:
System.out.println("Estudiantes: 3 libros, 15 días.");
System.out.println("PDI: 6 libros, 30 días.");
System.out.println("Visitantes: 1 libro, 7 días.");
System.out.println("Retraso: 0,20 € por día, como mucho 5 € (el PDI no paga).");
break;
default:
System.out.println("Opción no válida: " + opcion);
}
sc.close();
}
}
Por qué está hecho así:
- Llaves propias en
case 1: { … }ycase 2: { … }. Todo elswitches un solo bloque: si los dos casos declararanString tiposin llaves,javacdiríavariable tipo is already defined. Con llaves, cadatipovive solo dentro de su caso. Fíjate en que elbreakva dentro de las llaves, al final. - El
switchde dentro da valor a dos variables. Por eso es clásico (conbreak) y no una expresión: unswitchexpresión devuelve un valor. Eldefaultponedias = 0como marca de «tipo desconocido», igual que el -1 del certificado en la práctica. - El orden de la cadena del préstamo. Primero el tipo (si no existe,
maximoes 0 y el mensaje de «ya tienes 1 de 0» no tendría sentido), después el título, después el límite. Con el visitante:1 >= 1es verdadero, así que no puede llevarse otro. tiene + 1en el mensaje. Tenía 1 y se lleva otro: 2 de 3.- La multa con tope. 9 × 0,20 = 1,80 €; 40 × 0,20 = 8,00 €, que pasa de 5 y se queda en 5,00 €. Una alternativa de una línea es
Math.min(retraso * 0.20, 5). PDIen mayúsculas funciona porque se pasa a minúsculas al leerlo; sintoLowerCase(),"PDI".equals("pdi")seríafalsey el profesor pagaría multa.
En el caso 2, sc.nextInt() es la última lectura, así que no hace falta el nextLine() de limpieza: no hay nada después que pueda tragarse el Intro. En el caso 1 sí, porque después viene el título.
Solución Ejercicio 3
import java.util.Scanner;
/**
* Cajero automático: PIN con 3 intentos SIN bucle (un if dentro de otro) y una operación con switch.
* Se leen siempre líneas (nextLine + parseInt): así no hay trampa del buffer.
*/
public class CajeroPIN {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
final String PIN = "4321"; // String y no int: un PIN como 0420 perdería el cero
final int LIMITE = 600; // euros por operación
double saldo = 500.0;
int intentos = 3;
System.out.println("=== CAJERO AUTOMÁTICO ===");
System.out.print("PIN: ");
String pin = sc.nextLine().trim();
if (!pin.equals(PIN)) { // fallo 1
intentos--;
System.out.println("PIN incorrecto (" + intentos + " intentos restantes)");
System.out.print("PIN: ");
pin = sc.nextLine().trim();
if (!pin.equals(PIN)) { // fallo 2
intentos--;
System.out.println("PIN incorrecto (" + intentos + " intento restante)");
System.out.print("PIN: ");
pin = sc.nextLine().trim();
if (!pin.equals(PIN)) { // fallo 3: se acabó
System.out.println("Tarjeta bloqueada. Llama a tu banco.");
return; // sin esto, el programa seguiría hasta el menú
}
}
}
// Si llegamos aquí es porque algún intento acertó
System.out.println("PIN correcto.");
System.out.println();
System.out.println("1. Consultar saldo");
System.out.println("2. Sacar dinero");
System.out.println("3. Ingresar dinero");
System.out.print("Operación: ");
int op = Integer.parseInt(sc.nextLine().trim());
switch (op) {
case 1 -> System.out.printf("Saldo: %.2f €%n", saldo);
case 2 -> {
System.out.print("Cantidad (múltiplo de 10): ");
int cantidad = Integer.parseInt(sc.nextLine().trim());
// De lo más general a lo más concreto: formato, límite del cajero y, por último, el saldo
if (cantidad <= 0 || cantidad % 10 != 0) {
System.out.println("Cantidad no válida: el cajero solo da billetes de 10, 20 y 50.");
} else if (cantidad > LIMITE) {
System.out.println("Supera el límite por operación (" + LIMITE + " €).");
} else if (cantidad > saldo) {
System.out.printf("Saldo insuficiente: tienes %.2f €.%n", saldo);
} else {
saldo -= cantidad;
System.out.printf("Retira %d €. Saldo: %.2f €%n", cantidad, saldo);
}
}
case 3 -> {
System.out.print("Cantidad a ingresar: ");
int cantidad = Integer.parseInt(sc.nextLine().trim());
if (cantidad > 0 && cantidad <= 1000) {
saldo += cantidad;
System.out.printf("Ingreso hecho. Saldo: %.2f €%n", saldo);
} else {
System.out.println("Cantidad no válida (de 1 a 1000 € por ingreso).");
}
}
default -> System.out.println("Operación no válida.");
}
sc.close();
}
}
Por qué está hecho así:
- Tres
ifanidados. El segundo PIN solo se pide si el primero falló, y el tercero si fallaron los dos: cada intento va dentro del anterior. El dibujo enseña los caminos:
- El
returndel bloqueo es imprescindible. Sin él, tras el tercer fallo el programa saldría de los tresify llegaría a «PIN correcto.». En la práctica, el adivino usaba la otra forma (un bloque detrás de otro yreturnal acertar); las dos son válidas, y las dos piden a gritos un bucle. - El PIN como
String. Conint,0420se leería como 420 y un PIN de tres cifras pasaría por bueno. Además, comparar conequalsevita elNumberFormatExceptionsi alguien teclea letras. - Leer siempre líneas.
Integer.parseInt(sc.nextLine().trim())no deja ningún Intro en el buffer, así que no hace falta elnextLine()de limpieza en ningún sitio. - El orden de las comprobaciones de la retirada. Primero el formato (positiva y múltiplo de 10), después el límite del cajero y por último el saldo. Con 550: es múltiplo de 10 y no pasa de 600, así que llega a la tercera: 550 > 500, saldo insuficiente. Con 125:
125 % 10es 5, distinto de 0, y se para en la primera. - Las cuentas. 500 − 120 = 380,00 €; 500 + 200 = 700,00 €.
| Tecleado | Condición | Resultado |
|---|---|---|
1111 | !"1111".equals("4321") → true | intentos 3 → 2, se pide otro |
4321 | !"4321".equals("4321") → false | sale de los if: PIN correcto |
2, 120 | 120 % 10 == 0, 120 <= 600, 120 <= 500 | saldo 380,00 € |
Cuestionario tipo examen
Ahora ponte a prueba como en el examen. El cuestionario de Java de FP2 es así: 15 preguntas en 15 minutos sobre todo el temario de Java, de las que te tocan unas pocas de cada tema. Aquí tienes las de control de flujo: contesta sin ejecutar nada, corrige y lee la explicación de las que falles. Estas preguntas son también el banco del simulacro de teoría de FP2.
¿Qué imprime este fragmento?
int nota = 7;
if (nota >= 5) System.out.print("A");
else if (nota >= 7) System.out.print("B");
else System.out.print("C");
System.out.println();ABABC
Solución
Respuesta: A
Las condiciones se prueban de arriba abajo y se ejecuta la primera que se cumpla. Como 7 >= 5, sale A y el else if (nota >= 7) no llega a evaluarse. Por eso en una cadena de rangos se empieza por el más restrictivo.
¿Qué imprime este fragmento?
int dia = 2;
switch (dia) {
case 1: System.out.print("L");
case 2: System.out.print("M");
case 3: System.out.print("X"); break;
default: System.out.print("?");
}
System.out.println();Solución
Respuesta: MX
El switch clásico salta al case 2 e imprime M, pero como no hay break, sigue cayendo al case 3 (imprime X) hasta encontrar el break. Es el famoso fall-through: olvidar un break es uno de los errores más típicos.
Indica si es verdadero o falso:
- En Java no existe
elif: se escribeelse if. - Con
int x, la condiciónif (x = 5)no compila. - Desde Java 7 se puede hacer un
switchsobre unString. a && bevalúa siempre las dos partes.- En un
switchcon flechas (case 1 -> …) no hay fall-through.
Solución
- V — En Java no existe
elif: se escribeelse if. - V — Con
int x, la condiciónif (x = 5)no compila. - V — Desde Java 7 se puede hacer un
switchsobre unString. - F —
a && bevalúa siempre las dos partes. - V — En un
switchcon flechas (case 1 -> …) no hay fall-through.
if (x = 5) asigna en lugar de comparar y el resultado es un int, no un boolean: Java no lo acepta (en C sí compilaría). && es de cortocircuito: si la primera parte es falsa, la segunda no se evalúa. Y el switch con -> ejecuta solo su rama, sin necesitar break.
¿Qué imprime este fragmento?
int a = 4, b = 9; String r = (a > b) ? "a" : (b % 2 == 0 ? "b par" : "b impar"); System.out.println(r);
Solución
Respuesta: b impar
El operador ternario es condición ? siSí : siNo. 4 > 9 es falso, así que se evalúa el segundo ternario: 9 % 2 es 1, no 0, por lo que el resultado es "b impar". Es el equivalente a x if c else y de Python.
¿Qué imprime este fragmento?
String s = new String("hola");
if (s == "hola") System.out.println("igual");
else System.out.println("distinto");distintoigual- No compila
- Lanza una excepción
Solución
Respuesta: distinto
Con objetos, == mira si las dos variables apuntan al mismo sitio de memoria. Para comparar el texto se usa equals (o equalsIgnoreCase). Con String el fallo es traicionero porque a veces == acierta por casualidad (literales iguales comparten objeto).
¿Qué imprime este fragmento?
int x = 0;
if (x != 0 && 10 / x > 1) System.out.println("grande");
else System.out.println("seguro");Solución
Respuesta: seguro
x != 0 es falso, así que && ya sabe que todo es falso y no evalúa 10 / x. Gracias a eso no hay ArithmeticException por dividir entre cero. Con & (sin cortocircuito) sí se evaluaría y el programa fallaría.
Completa las reglas del if en Java:
La condición va siempre entre ____ y tiene que ser de tipo ____. El bloque va entre ____; si se omiten, del if solo depende la ____ sentencia.
Palabras:
- paréntesis
- boolean
- primera
- corchetes
- sangría
- última
- llaves
- int
Solución
La condición va siempre entre paréntesis y tiene que ser de tipo boolean. El bloque va entre llaves; si se omiten, del if solo depende la primera sentencia.
Sin llaves, la sangría no significa nada para Java: solo la primera sentencia queda dentro del if y las siguientes se ejecutan siempre. Por eso se recomienda poner llaves aunque haya una sola línea.
¿Qué imprime este fragmento (Java 14 o posterior)?
String dia = "sab";
String tipo = switch (dia) {
case "sab", "dom" -> "finde";
default -> "laborable";
};
System.out.println(tipo);Solución
Respuesta: finde
El switch moderno puede devolver un valor y agrupar varios casos con comas. "sab" encaja en el primer caso, así que tipo vale "finde". Fíjate en el ; final: todo el switch es una expresión dentro de una asignación.
Relaciona cada operador con lo que hace:
&& | |
|| | |
! | |
== | |
% |
Definiciones:
- Resto de la división entera
- O lógico con cortocircuito
- Potencia
- Concatenación de condiciones
- Y lógico con cortocircuito
- Igualdad (en objetos compara referencias)
- Negación
Solución
&&→ Y lógico con cortocircuito||→ O lógico con cortocircuito!→ Negación==→ Igualdad (en objetos compara referencias)%→ Resto de la división entera
En Java no se escribe and, or ni not como en Python: son &&, || y !. La potencia no es un operador (Math.pow(a, b)) y % da el resto (9 % 2 es 1).
¿Qué imprime este fragmento?
int t = 20;
if (t > 25)
System.out.print("calor ");
System.out.print("playa");
System.out.println();playa- Una línea vacía
calor playa- No compila
Solución
Respuesta: playa
La sangría hace creer que las dos líneas van dentro del if, pero Java solo mira las llaves. Como no hay, del if depende únicamente print("calor "). Es el error de tener un if sin llaves al que luego se le añade una línea.
Con int a = 5, b = 0;, ¿qué condiciones son verdaderas?
a > 3 && b == 0a < 3 || b == 0a % 2 == 1!(a == 5)b != 0 && a / b > 1
Solución
Respuesta: a > 3 && b == 0 · a < 3 || b == 0 · a % 2 == 1
&& necesita las dos partes ciertas; ||, al menos una. 5 % 2 es 1, así que la tercera es cierta. La última es falsa sin lanzar ninguna excepción gracias al cortocircuito.
Míralo paso a paso en Python Tutor
Python Tutor (pythontutor.com) ejecuta el código línea a línea y marca cuál se ejecuta en cada paso. Elige Java en el desplegable y pega este programa, que clasifica el mismo 9,5 con el orden equivocado y con el bueno:
public class TutorNota {
public static void main(String[] args) {
double nota = 9.5;
String mal;
if (nota >= 5) {
mal = "Suficiente";
} else if (nota >= 9) {
mal = "Muy bueno";
} else {
mal = "No apto";
}
String bien;
if (nota >= 9) {
bien = "Muy bueno";
} else if (nota >= 5) {
bien = "Suficiente";
} else {
bien = "No apto";
}
System.out.println(mal + " / " + bien);
}
}
Salida:
Suficiente / Muy bueno
Qué observar paso a paso:
- En la primera cadena, la flecha pasa por
if (nota >= 5), entra y salta directamente a la línea deString bien;: loselse ifni se visitan. - En el panel de variables aparece
mal = "Suficiente"mientrasnotasigue valiendo 9.5. - En la segunda cadena, la primera condición (
nota >= 9) ya es verdadera: entra a la primera. - Cuenta los pasos de cada cadena: las dos se paran en cuanto encuentran una condición verdadera. Lo que cambia es cuál encuentran primero.
Chuletario: control de flujo en Java
Lo esencial en 30 segundos: valida el dato y corta con return antes de calcular; en una cadena else if gana la primera condición verdadera, así que lo más restrictivo va primero; los textos se comparan con equals(); «fuera de rango» es || y «dentro» es &&; en un switch clásico cada caso necesita su break (o usa flechas); leer líneas con nextLine() + parseInt evita la trampa del buffer.
| Quiero… | Código | Resultado |
|---|---|---|
| Comprobar que está dentro de un rango | if (x >= 0 && x <= 14) | las dos a la vez |
| Detectar que está fuera | if (x < 0 || x > 14) | basta con una |
| Clasificar por tramos | if (n >= 11) … else if (n >= 9) … else … | de mayor a menor |
| Comparar textos | a.equals(b) / a.equalsIgnoreCase(b) | true si dicen lo mismo |
Comparar con un fijo sin riesgo de null | "s".equalsIgnoreCase(r) | nunca lanza excepción |
| Terminar el programa antes | return; dentro de main | se acaba ahí |
| Elegir según un valor | switch (op) { case 1 -> …; default -> …; } | sin fall-through |
| Calcular según un valor | int d = switch (t) { case "a" -> 15; default -> 0; }; | default obligatorio |
| Acumular a propósito | case 0: x += p; case 1: x += p; break; | cae de un caso al siguiente |
Variable propia en un case | case 1: { String t = …; break; } | llaves propias |
| Leer un número sin trampa | Integer.parseInt(sc.nextLine().trim()) | no deja Intro |
| Un número al azar repetible | new Random(42).nextInt(100) + 1 | de 1 a 100, siempre el mismo |
Plantillas de los ejercicios:
| Ejercicio | Patrón | Con estos datos | Sale |
|---|---|---|---|
| Nota de acceso | validar + return, else if de mayor a menor, printf("%.2f") | 11,8 | Excelente, 1,30 por encima |
| Biblioteca | switch del menú con llaves por caso + switch de String | estudiante, 1 libro | 15 días (2 de 3 libros) |
| Biblioteca (multa) | if con tope | 40 días de retraso | 5,00 € |
| Cajero | tres if (!pin.equals(PIN)) anidados + return | 1111, 4321, sacar 120 | saldo 380,00 € |
| Síntoma | Causa probable | ||
|---|---|---|---|
| Notas altas clasificadas como bajas | else if ordenados de menor a mayor con solo el límite de abajo | ||
| Dos textos iguales «no coinciden» | comparados con == en lugar de equals() | ||
Un switch da el valor del último caso | falta el break (fall-through) | ||
| Se aceptan datos imposibles | && donde iba ` | ` (la condición nunca es verdadera) | |
variable … is already defined en un switch | dos case declaran la misma variable sin llaves propias | ||
variable … might not have been initialized | alguna rama del if no da valor a la variable | ||
| El programa sigue después del bloqueo | falta el return; | ||
InputMismatchException con un decimal | se tecleó punto en un equipo en español (o al revés) |
Antes de entregar, pregúntate:
- ¿He probado un valor de cada rama, también los límites (5, 7, 9, 11) y uno fuera de rango?
- ¿Todos los textos se comparan con
equals()? - ¿Cada
casede unswitchclásico termina enbreak, salvo los que caen a propósito? - ¿Valido el dato antes de usarlo (antes de
substring, de dividir o de clasificar)?
Con esto cierras el control de flujo. En el tema siguiente, bucles en Java, el adivino y el cajero dejan de copiar su bloque tres veces: un while repite lo que haga falta.
¿Te ha servido este artículo? Así puedes apoyar SergioLearns →




Simulacro del examen práctico de IC2 — diagrama de flujo, fork e hilos en C (y las capturas que no pueden faltar)