Control de flujo en Java — ejercicios con validaciones, menús y bucles

En la práctica combinaste Scanner, if/else y switch en programas que se parecían a cosas reales. Ahora toca resolver por tu cuenta. Los tres ejercicios giran alrededor de la misma idea que ya viste: validar entrada, tomar decisiones según varias condiciones y controlar un bucle que depende de lo que escribe el usuario.

Intenta resolverlos antes de mirar la solución. Si te atascas más de 10 minutos, usa la pista.


Control de flujo en Java ejercicios — Nivel básico

Ejercicio 1 — Validador de nota de acceso

Escribe un programa que pida una nota de acceso a la universidad (0-14 en el sistema español) y clasifique al estudiante según el resultado. Además, indica si esa nota da acceso a un grado con nota de corte de 10.5, un valor típico en carreras con demanda alta como GCID.

Salida esperada con nota 11.8:

=== VALIDADOR DE NOTA DE ACCESO ===

Nota de acceso (0-14): 11.8

--- Resultado ---
Clasificación: Notable
¿Accede a un grado con corte 10.5? SÍ ✓
Margen sobre el corte: 1.30 puntos

Clasificación:

< 5           → No apto
5 - 6.9       → Suficiente
7 - 8.9       → Notable
9 - 10.9      → Muy bueno
>= 11         → Excelente

Si la nota está fuera de 0-14, el programa debe avisar de que el dato no es válido y no seguir calculando nada más.

No calcules la clasificación antes de validar el rango. Si validas primero y cortas con return cuando el dato no tiene sentido, el resto del código puede asumir tranquilamente que la nota está entre 0 y 14 sin comprobarlo de nuevo.

💡 Pistas:

  • Valida el rango con un if al principio; si falla, corta ahí con un mensaje y no calcules el resto
  • Usa if/else if en cadena para la clasificación — son rangos, no valores exactos, así que switch no encaja aquí
  • El margen sobre el corte es simplemente nota - 10.5, y solo tiene sentido mostrarlo si el resultado es positivo

Control de flujo en Java ejercicios — Nivel intermedio

Ejercicio 2 — Menú de biblioteca

Escribe un programa que gestione una biblioteca pequeña con un array de hasta 5 libros. El menú debe permitir añadir un libro, marcarlo como prestado, devolverlo y ver el estado de todos.

Salida esperada de una sesión completa:

=== BIBLIOTECA ===
1. Añadir libro
2. Prestar libro
3. Devolver libro
4. Ver todos
5. Salir
Opción: 1
Título: Clean Code
✓ Libro añadido

1. Añadir libro
2. Prestar libro
3. Devolver libro
4. Ver todos
5. Salir
Opción: 2
Título a prestar: Clean Code
✓ Prestado

1. Añadir libro
2. Prestar libro
3. Devolver libro
4. Ver todos
5. Salir
Opción: 4
--- Estado de la biblioteca ---
1. Clean Code — Prestado

1. Añadir libro
2. Prestar libro
3. Devolver libro
4. Ver todos
5. Salir
Opción: 5
Hasta luego

Si intentas prestar un libro que ya está prestado, o que no existe, el programa debe avisarlo sin romperse.

Si declaras una variable dentro de un case sin llaves propias, y otro case del mismo switch declara una variable con el mismo nombre, Java da error de compilación — aunque nunca se ejecuten los dos casos a la vez. Es la razón por la que en la solución los case 2 y case 3 van entre { }.

💡 Pistas:

  • Guarda dos arrays paralelos: String[] titulos y boolean[] prestado, ambos de tamaño 5, con la misma idea de «contador» que usaste en la práctica
  • Para buscar un libro por título, recorre el array con un for y compara con .equals(), nunca con ==
  • El bucle principal es un do-while, igual que en la práctica: siempre se muestra el menú al menos una vez

Control de flujo en Java ejercicios — Desafío final

Ejercicio 3 — Cajero automático con PIN

Escribe un programa que simule un cajero: primero pide un PIN de 4 dígitos y da un máximo de 3 intentos antes de bloquear la sesión. Si el PIN es correcto, muestra un menú para consultar saldo, ingresar dinero, retirar dinero y salir, repitiendo hasta que el usuario elija salir.

Salida esperada:

=== CAJERO AUTOMÁTICO ===

Introduce tu PIN: 1111
PIN incorrecto (2 intentos restantes)
Introduce tu PIN: 2222
PIN incorrecto (1 intento restante)
Introduce tu PIN: 4321
✓ Acceso concedido

--- Menú ---
1. Consultar saldo
2. Ingresar dinero
3. Retirar dinero
4. Salir
Opción: 1
Saldo actual: 500,00 €

--- Menú ---
1. Consultar saldo
2. Ingresar dinero
3. Retirar dinero
4. Salir
Opción: 3
Cantidad a retirar: 700
✗ Saldo insuficiente (disponible: 500,00 €)

--- Menú ---
1. Consultar saldo
2. Ingresar dinero
3. Retirar dinero
4. Salir
Opción: 4
Sesión finalizada

El PIN correcto es 4321 y el saldo inicial es 500.0. Si se agotan los 3 intentos sin acertar, el programa debe terminar sin mostrar el menú, con un mensaje de tarjeta bloqueada.

💡 Pistas:

  • El bloque del PIN es un while que se repite mientras el intento sea incorrecto y aún queden intentos — dos condiciones con &&
  • Usa una variable intentos que empiece en 3 y se reste 1 cada vez que falles, y comprueba si llegó a 0 para decidir si bloqueas o dejas pasar
  • El menú de operaciones es un do-while con switch, igual que en el Ejercicio 2 y en la práctica

Cuando un bucle depende de varias condiciones con &&, como intentos > 0 && !acceso, no hace falta que evalúes tú mismo el resultado combinado. Java comprueba cada parte en orden y, en cuanto una es falsa, ni siquiera mira las siguientes — así que el orden de las condiciones puede ahorrar trabajo si pones primero la que falla más a menudo.


¿Qué falla aquí?

Antes de las soluciones, tres errores típicos de esta parte del temario. ¿Ves el problema en cada uno?

Error 1

Scanner sc = new Scanner(System.in);
System.out.print("Nota: ");
double nota = sc.nextDouble();
System.out.print("Comentario: ");
String comentario = sc.nextLine();  // ← se salta la pregunta

Falta el sc.nextLine() de limpieza después de nextDouble(). Sin él, el segundo nextLine() lee el salto de línea sobrante en vez de esperar tu respuesta:

double nota = sc.nextDouble();
sc.nextLine();  // ← consume el \n sobrante
String comentario = sc.nextLine();  // ahora sí espera

Error 2

int opcion = 2;
switch (opcion) {
    case 1:
        System.out.println("Uno");
    case 2:
        System.out.println("Dos");
    case 3:
        System.out.println("Tres");
        break;
}
// Imprime "Dos" y "Tres" — no solo "Dos"

Faltan los break en los casos 1 y 2, así que el programa cae en fall-through desde case 2 hasta el primer break que encuentra. La solución es poner break; al final de cada caso, salvo que el fall-through sea intencionado:

case 2:
    System.out.println("Dos");
    break;  // ← ahora sí se detiene aquí

Error 3

String respuesta = sc.nextLine();
if (respuesta == "si") {
    System.out.println("Confirmado");
}
// Nunca entra, aunque escribas exactamente "si"

== compara referencias de memoria en los Strings, no el contenido. La comparación correcta es siempre con .equals():

if (respuesta.equals("si")) {  // ← correcto

Soluciones comentadas

Solución Ejercicio 1

import java.util.Scanner;

public class ValidadorNota {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== VALIDADOR DE NOTA DE ACCESO ===");
        System.out.println();

        System.out.print("Nota de acceso (0-14): ");
        double nota = sc.nextDouble();

        // Cortamos aquí mismo si el dato no tiene sentido — no seguimos calculando nada
        if (nota < 0 || nota > 14) {
            System.out.println("✗ La nota debe estar entre 0 y 14");
            sc.close();
            return;
        }

        // Clasificación por rangos — if/else if, no switch, porque son intervalos de double
        String clasificacion;
        if (nota < 5) {
            clasificacion = "No apto";
        } else if (nota < 7) {
            clasificacion = "Suficiente";
        } else if (nota < 9) {
            clasificacion = "Notable";
        } else if (nota < 11) {
            clasificacion = "Muy bueno";
        } else {
            clasificacion = "Excelente";
        }

        // Nota de corte fija para este ejercicio — en la práctica variaría según el grado
        double corte = 10.5;
        boolean accede = nota >= corte;

        System.out.println();
        System.out.println("--- Resultado ---");
        System.out.printf("Clasificación: %s%n", clasificacion);
        // El operador ternario aquí evita un if/else completo para un solo texto
        System.out.printf("¿Accede a un grado con corte %.1f? %s%n",
                          corte, accede ? "SÍ ✓" : "NO ✗");

        // El margen solo tiene sentido mostrarlo si de verdad se accede
        if (accede) {
            System.out.printf("Margen sobre el corte: %.2f puntos%n", nota - corte);
        }

        sc.close();
    }
}

Solución Ejercicio 2

import java.util.Scanner;

public class Biblioteca {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // Arrays paralelos: la posición i de uno corresponde a la posición i del otro
        String[] titulos = new String[5];
        boolean[] prestado = new boolean[5];
        int contador = 0;    // cuántos libros hay realmente guardados

        int opcion;
        do {
            System.out.println("\n=== BIBLIOTECA ===");
            System.out.println("1. Añadir libro");
            System.out.println("2. Prestar libro");
            System.out.println("3. Devolver libro");
            System.out.println("4. Ver todos");
            System.out.println("5. Salir");
            System.out.print("Opción: ");
            opcion = sc.nextInt();
            sc.nextLine();    // limpiador — ya sabes por qué

            switch (opcion) {
                case 1:
                    if (contador < titulos.length) {
                        System.out.print("Título: ");
                        titulos[contador] = sc.nextLine();
                        prestado[contador] = false;    // nuevo libro, disponible por defecto
                        contador++;
                        System.out.println("✓ Libro añadido");
                    } else {
                        System.out.println("✗ La biblioteca está llena");
                    }
                    break;

                case 2: {
                    System.out.print("Título a prestar: ");
                    String buscado = sc.nextLine();
                    int indice = -1;    // -1 significa "todavía no encontrado"
                    for (int i = 0; i < contador; i++) {
                        if (titulos[i].equalsIgnoreCase(buscado)) {
                            indice = i;
                            break;    // encontrado — no hace falta seguir buscando
                        }
                    }
                    if (indice == -1) {
                        System.out.println("✗ Ese libro no existe");
                    } else if (prestado[indice]) {
                        System.out.println("✗ Ya está prestado");
                    } else {
                        prestado[indice] = true;
                        System.out.println("✓ Prestado");
                    }
                    break;
                }

                case 3: {
                    System.out.print("Título a devolver: ");
                    String buscado = sc.nextLine();
                    int indice = -1;
                    for (int i = 0; i < contador; i++) {
                        if (titulos[i].equalsIgnoreCase(buscado)) {
                            indice = i;
                            break;
                        }
                    }
                    if (indice == -1) {
                        System.out.println("✗ Ese libro no existe");
                    } else if (!prestado[indice]) {
                        System.out.println("✗ Ese libro no estaba prestado");
                    } else {
                        prestado[indice] = false;
                        System.out.println("✓ Devuelto");
                    }
                    break;
                }

                case 4:
                    if (contador == 0) {
                        System.out.println("La biblioteca está vacía");
                    } else {
                        System.out.println("--- Estado de la biblioteca ---");
                        for (int i = 0; i < contador; i++) {
                            String estado = prestado[i] ? "Prestado" : "Disponible";
                            System.out.printf("%d. %s — %s%n", i + 1, titulos[i], estado);
                        }
                    }
                    break;

                case 5:
                    System.out.println("Hasta luego");
                    break;

                default:
                    System.out.println("✗ Opción no válida");
            }
        } while (opcion != 5);

        sc.close();
    }
}

Fíjate en los casos 2 y 3: van entre llaves { } aparte de las del switch. Sin ellas, declarar int indice en dos case distintos daría un error de variable duplicada, porque técnicamente seguirían en el mismo bloque. Las llaves crean un ámbito propio para cada case.

Solución Ejercicio 3

import java.util.Scanner;

public class CajeroPIN {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        final String pinCorrecto = "4321";
        double saldo = 500.0;

        System.out.println("=== CAJERO AUTOMÁTICO ===");
        System.out.println();

        int intentos = 3;
        boolean acceso = false;

        // Seguimos pidiendo PIN mientras queden intentos Y no hayamos acertado
        while (intentos > 0 && !acceso) {
            System.out.print("Introduce tu PIN: ");
            String pin = sc.nextLine();

            if (pin.equals(pinCorrecto)) {
                acceso = true;
                System.out.println("✓ Acceso concedido");
            } else {
                intentos--;
                if (intentos > 0) {
                    System.out.printf("PIN incorrecto (%d intento%s restante%s)%n",
                                      intentos, intentos == 1 ? "" : "s", intentos == 1 ? "" : "s");
                }
            }
        }

        // Si salimos del while sin acceso, es porque se agotaron los intentos
        if (!acceso) {
            System.out.println("✗ Tarjeta bloqueada — demasiados intentos fallidos");
            sc.close();
            return;    // termina el programa — nunca llega al menú
        }

        int opcion;
        do {
            System.out.println("\n--- Menú ---");
            System.out.println("1. Consultar saldo");
            System.out.println("2. Ingresar dinero");
            System.out.println("3. Retirar dinero");
            System.out.println("4. Salir");
            System.out.print("Opción: ");
            opcion = sc.nextInt();

            switch (opcion) {
                case 1:
                    System.out.printf("Saldo actual: %.2f €%n", saldo);
                    break;

                case 2:
                    System.out.print("Cantidad a ingresar: ");
                    double ingreso = sc.nextDouble();
                    saldo += ingreso;
                    System.out.printf("✓ Ingreso realizado. Saldo: %.2f €%n", saldo);
                    break;

                case 3:
                    System.out.print("Cantidad a retirar: ");
                    double retiro = sc.nextDouble();
                    if (retiro > saldo) {
                        System.out.printf("✗ Saldo insuficiente (disponible: %.2f €)%n", saldo);
                    } else {
                        saldo -= retiro;
                        System.out.printf("✓ Retirada realizada. Saldo: %.2f €%n", saldo);
                    }
                    break;

                case 4:
                    System.out.println("Sesión finalizada");
                    break;

                default:
                    System.out.println("✗ Opción no válida");
            }
        } while (opcion != 4);

        sc.close();
    }
}

El return; después de bloquear la tarjeta es la pieza clave: corta el programa ahí mismo y evita que el flujo siga hasta el menú, exactamente igual que hiciste con la validación de rango en el Ejercicio 1.


Visualízalo con Python Tutor

Selecciona Java en el desplegable y pega en pythontutor.com:

public class Demo {
    public static void main(String[] args) {
        String pinCorrecto = "4321";
        int intentos = 3;
        boolean acceso = false;

        String[] pruebas = {"1111", "2222", "4321"};
        int i = 0;

        while (intentos > 0 && !acceso && i < pruebas.length) {
            String pin = pruebas[i];
            if (pin.equals(pinCorrecto)) {
                acceso = true;
            } else {
                intentos--;
            }
            i++;
        }

        System.out.println("Acceso: " + acceso);
        System.out.println("Intentos restantes: " + intentos);
    }
}

Ejecuta paso a paso y fíjate en la condición del while: tiene tres partes unidas con &&, y basta con que una sea falsa para que el bucle pare. Observa cómo intentos baja de 3 a 2 y a 1 con los dos primeros PIN fallidos, y cómo en la tercera vuelta pin.equals(pinCorrecto) da true, acceso pasa a true, y aunque intentos (1) siga siendo mayor que 0, el bucle termina porque ya no se cumplen las tres condiciones a la vez.


Chuletario — control de flujo en Java

// ============================================
// CHULETARIO — Control de flujo en Java
// Sergio Learns · sergiolearns.com
// ============================================

// VALIDAR ANTES DE SEGUIR — cortar el flujo pronto
if (dato < minimo || dato > maximo) {
    System.out.println("Dato no válido");
    return;    // o break/continue según el contexto
}
// el resto del código asume ya que el dato es correcto

// IF/ELSE IF — para rangos y condiciones complejas
if (x < 5) {
    ...
} else if (x < 10) {
    ...
} else {
    ...
}

// SWITCH — para valores exactos (int, char, String)
switch (variable) {
    case valor1:
        // código
        break;              // obligatorio — evita el fall-through
    case valor2:
    case valor3:            // varios casos, misma acción
        // código
        break;
    default:
        // código
}

// LLAVES PROPIAS DENTRO DE UN CASE
case 1: {
    int x = 10;    // ámbito propio — no choca con otra declaración de x
    break;
}

// WHILE CON VARIAS CONDICIONES
while (intentos > 0 && !acceso) {
    ...
}
// se repite mientras TODAS las condiciones con && sean verdaderas
// basta con que UNA sea falsa para salir

// DO-WHILE PARA MENÚS
int opcion;
do {
    // mostrar menú
    opcion = sc.nextInt();
    switch (opcion) { ... }
} while (opcion != salida);

// ARRAYS PARALELOS
String[] nombres = new String[5];
boolean[] estado  = new boolean[5];
// misma posición i en los dos arrays = mismo elemento

// BUSCAR EN UN ARRAY
int indice = -1;              // -1 = "todavía no encontrado"
for (int i = 0; i < contador; i++) {
    if (array[i].equals(buscado)) {
        indice = i;
        break;                 // encontrado — no seguir buscando
    }
}

// SCANNER — la trampa de siempre
int n = sc.nextInt();
sc.nextLine();                 // ← limpiador obligatorio antes de nextLine()
String texto = sc.nextLine();

// STRINGS — comparar siempre con equals
s1.equals(s2)                  // ← correcto
s1 == s2                       // ← peligroso, compara referencias

// OPERADOR TERNARIO — para un solo valor según una condición
String texto = (condicion) ? "si" : "no";
Sigue aprendiendoTodos los artículos de Java

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *