Bucles en Java — ejercicios con vocales, notas y el bucle que se me colgó a mí
Cuando yo estaba con esto, el primer bucle anidado que escribí se quedó colgado. No fue un bucle infinito por olvidar la condición — esa ya lo tenía controlado — fue que actualizaba el contador del bucle de fuera, dentro del bucle de dentro, así que el de fuera nunca avanzaba. Tuve que forzar el cierre de la terminal. Si te pasa algo parecido con estos ejercicios, no es que se te dé mal, es que los bucles anidados tienen esa trampa y hay que verla una vez para no volver a caer.
Aquí tienes tres ejercicios de bucles en Java, de menos a más complicado. El último usa un bucle dentro de otro, así que tómate tu tiempo con él.
Tabla de Contenidos
Bucles en Java ejercicios — Nivel básico
Ejercicio 1 — Contador de vocales
Escribe un programa que pida una palabra o frase y cuente cuántas vocales tiene, indicando además cuántas veces aparece cada una (a, e, i, o, u).
Salida esperada con la palabra programacion:
=== CONTADOR DE VOCALES === Palabra o frase: programacion --- Resultado --- Total de vocales: 6 a: 2 e: 0 i: 1 o: 2 u: 0
El programa no debe distinguir mayúsculas de minúsculas: «Programacion» y «programacion» deben dar el mismo resultado.
Recorrer la palabra con for (int i = 0; i <= palabra.length(); i++) en vez de i < palabra.length(). Con <= el bucle intenta leer una posición que no existe (la longitud coincide con el primer índice fuera de rango) y el programa se para con StringIndexOutOfBoundsException. Los índices de un String van de 0 a longitud - 1, igual que en un array.
💡 Pistas:
- Recorre la palabra carácter a carácter con un
forypalabra.charAt(i) - Convierte cada carácter a minúscula con
Character.toLowerCase(c)antes de compararlo, así te ahorras comprobar las dos versiones - Usa cinco contadores
int, uno por vocal, y súmalos todos al final para el total — o suma directamente un contador general a la vez que el de cada vocal
Bucles en Java ejercicios — Nivel intermedio
Ejercicio 2 — Notas con valor centinela
Escribe un programa que vaya pidiendo notas una a una y las acumule, hasta que el usuario escriba -1, que es la señal de que ha terminado. Al final debe mostrar cuántas notas se metieron, la media, la nota más alta y la más baja.
Salida esperada de una sesión con las notas 6, 8, 4 y 9:
=== NOTAS DE LA ASIGNATURA === Escribe las notas una a una. Escribe -1 para terminar. Nota: 6 Nota: 8 Nota: 4 Nota: 9 Nota: -1 --- Resultado --- Notas introducidas: 4 Media: 6.75 Nota más alta: 9.0 Nota más baja: 4.0
Si el usuario escribe -1 en la primera nota, sin haber metido ninguna, el programa debe avisar de que no hay datos y no debe intentar calcular la media, porque dividir entre 0 notas no tiene sentido.
Este es el típico caso donde un do-while encaja mejor que un while normal, porque necesitas pedir la nota antes de poder comprobar si es el centinela -1 — no puedes comprobar la condición de salida sin haber leído ya un dato.
💡 Pistas:
- Usa un
do-whileque siga pidiendo notas mientras la última introducida no sea-1 - Para la nota más alta y la más baja, no hace falta guardar todas las notas en un array: basta con dos variables
maximayminimaque vayas actualizando cada vez que entra una nota mejor o peor que la guardada - Inicializa
maximayminimacon la primera nota real que se introduzca, no con0, porque si todas las notas son bajas,0nunca se actualizaría como mínimo
Bucles en Java ejercicios — Desafío final
Ejercicio 3 — Números primos en un rango
Escribe un programa que pida dos números enteros, un inicio y un final, y muestre todos los números primos que hay en ese rango (incluidos los dos extremos). Un número es primo si solo es divisible entre 1 y entre sí mismo, y por convención el 1 no cuenta como primo.
Salida esperada con rango 10 a 30:
=== NÚMEROS PRIMOS EN UN RANGO === Desde: 10 Hasta: 30 --- Primos encontrados --- 11 13 17 19 23 29 Total: 6 primos
Si el número de inicio es mayor que el de fin, el programa debe avisar de que el rango no es válido y no debe intentar buscar nada.
Para comprobar si un número n es primo no hace falta probar todos los divisores hasta n - 1. Basta con llegar hasta la raíz cuadrada de n, porque si n tuviera un divisor mayor que su raíz cuadrada, forzosamente tendría también uno menor, y ya lo habrías encontrado antes. Con números pequeños como estos no se nota la diferencia, pero es la razón por la que en programas reales las búsquedas de primos no llegan hasta el número entero.
💡 Pistas:
- El bucle de fuera recorre cada número del rango con un
for; el bucle de dentro comprueba si ese número tiene algún divisor - En el bucle de dentro, en cuanto encuentres un divisor puedes cortar con
break— ya sabes que no es primo, seguir comprobando es trabajo de más - Una forma cómoda de saber si, tras el bucle de dentro, el número resultó primo es con una variable
boolean esPrimoque empieza entruey solo pasa afalsesi se encuentra un divisor
¿Qué falla aquí?
Antes de las soluciones, tres errores típicos de esta parte del temario. ¿Ves el problema en cada uno?
Error 1
java
int i = 0;
while (i < 5) {
System.out.println("Vuelta " + i);
}
// Se queda colgado para siempre
Falta i++ dentro del bucle. La condición i < 5 nunca deja de cumplirse porque i nunca cambia, así que el programa entra en un bucle infinito. Es exactamente el error que yo cometí con mi primer bucle anidado, solo que aquí es más fácil de ver:
java
int i = 0;
while (i < 5) {
System.out.println("Vuelta " + i);
i++; // ← sin esto, i siempre vale 0
}
Error 2
for (int i = 0; i < 10; i++) {
if (i == 5) {
break;
}
System.out.println(i);
}
// ¿Imprime hasta el 9, o se para antes?
Este no es un error de compilación ni de ejecución, es un error de intención: si lo que se quería era saltarse solo el 5 y seguir con el 6, 7, 8 y 9, break está mal — break corta el bucle entero, no salta una vuelta. Lo que hace falta ahí es continue, que sí se salta solo la vuelta actual y sigue con la siguiente:
for (int i = 0; i < 10; i++) {
if (i == 5) {
continue; // ← se salta el 5, pero sigue con el 6, 7, 8, 9
}
System.out.println(i);
}
Error 3
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 3; i++) { // ← mismo nombre de variable
System.out.println(i);
}
}
// No compila
Java no deja declarar dos variables con el mismo nombre en bucles anidados si uno está dentro del otro, porque el i de dentro estaría «tapando» al i de fuera dentro de su propio ámbito, y Java lo prohíbe directamente en vez de dejar que te confundas. La solución es usar nombres distintos, normalmente i para el bucle de fuera y j para el de dentro:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) { // ← nombre distinto
System.out.println(i + "," + j);
}
}
Soluciones comentadas
Solución Ejercicio 1
import java.util.Scanner;
public class ContadorVocales {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== CONTADOR DE VOCALES ===");
System.out.print("Palabra o frase: ");
String texto = sc.nextLine();
// Un contador por cada vocal — todos arrancan en 0
int contA = 0, contE = 0, contI = 0, contO = 0, contU = 0;
// Recorremos el texto carácter a carácter
for (int i = 0; i < texto.length(); i++) {
// toLowerCase evita tener que comparar mayúscula y minúscula por separado
char c = Character.toLowerCase(texto.charAt(i));
// switch sobre un char funciona igual que sobre un int o un String
switch (c) {
case 'a':
contA++;
break;
case 'e':
contE++;
break;
case 'i':
contI++;
break;
case 'o':
contO++;
break;
case 'u':
contU++;
break;
// si no es ninguna vocal, no hacemos nada — no hace falta default
}
}
// El total es la suma de los cinco contadores individuales
int total = contA + contE + contI + contO + contU;
System.out.println();
System.out.println("--- Resultado ---");
System.out.println("Total de vocales: " + total);
System.out.println("a: " + contA);
System.out.println("e: " + contE);
System.out.println("i: " + contI);
System.out.println("o: " + contO);
System.out.println("u: " + contU);
sc.close();
}
}
Solución Ejercicio 2
import java.util.Scanner;
public class NotasCentinela {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== NOTAS DE LA ASIGNATURA ===");
System.out.println("Escribe las notas una a una. Escribe -1 para terminar.");
double suma = 0;
int contador = 0;
double maxima = 0;
double minima = 0;
double nota;
// do-while porque necesitamos leer la primera nota antes de poder
// comprobar si es el centinela -1
do {
System.out.print("Nota: ");
nota = sc.nextDouble();
// Si es el centinela, no la procesamos como una nota más
if (nota != -1) {
suma += nota;
// La primera nota real inicializa máxima y mínima;
// las siguientes solo actualizan si superan el valor guardado
if (contador == 0) {
maxima = nota;
minima = nota;
} else {
if (nota > maxima) {
maxima = nota;
}
if (nota < minima) {
minima = nota;
}
}
contador++;
}
} while (nota != -1);
System.out.println();
System.out.println("--- Resultado ---");
// Sin notas, calcular la media dividiría entre 0 — lo evitamos
if (contador == 0) {
System.out.println("No se introdujo ninguna nota");
} else {
double media = suma / contador;
System.out.println("Notas introducidas: " + contador);
System.out.printf("Media: %.2f%n", media);
System.out.println("Nota más alta: " + maxima);
System.out.println("Nota más baja: " + minima);
}
sc.close();
}
}
Solución Ejercicio 3
import java.util.Scanner;
public class PrimosEnRango {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("=== NÚMEROS PRIMOS EN UN RANGO ===");
System.out.print("Desde: ");
int inicio = sc.nextInt();
System.out.print("Hasta: ");
int fin = sc.nextInt();
// Cortamos aquí mismo si el rango no tiene sentido
if (inicio > fin) {
System.out.println("✗ El inicio no puede ser mayor que el fin");
sc.close();
return;
}
System.out.println();
System.out.println("--- Primos encontrados ---");
int totalPrimos = 0;
// Bucle de fuera: recorre cada número del rango
for (int numero = inicio; numero <= fin; numero++) {
// El 1 no cuenta como primo por convención, así que lo saltamos
if (numero < 2) {
continue;
}
boolean esPrimo = true;
// Bucle de dentro: busca un divisor de "numero"
// Basta con llegar hasta su raíz cuadrada — si no hay divisor
// hasta ahí, tampoco lo habrá más allá
for (int divisor = 2; divisor <= Math.sqrt(numero); divisor++) {
if (numero % divisor == 0) {
esPrimo = false;
break; // ya sabemos que no es primo, no hace falta seguir
}
}
if (esPrimo) {
System.out.println(numero);
totalPrimos++;
}
}
System.out.println("Total: " + totalPrimos + " primos");
sc.close();
}
}
Fíjate en el break del bucle de dentro: en cuanto se encuentra un divisor, ya no hace falta seguir probando los siguientes — el número no es primo y seguir comprobando sería trabajo perdido. Es la misma idea que el break del Ejercicio 2 de control de flujo, cuando ya habías encontrado el libro que buscabas.
Visualízalo con Python Tutor
Selecciona Java en el desplegable y pega esto en pythontutor.com:
public class Demo {
public static void main(String[] args) {
int totalPrimos = 0;
for (int numero = 10; numero <= 15; numero++) {
boolean esPrimo = true;
for (int divisor = 2; divisor <= Math.sqrt(numero); divisor++) {
if (numero % divisor == 0) {
esPrimo = false;
break;
}
}
if (esPrimo) {
totalPrimos++;
}
}
System.out.println("Primos encontrados: " + totalPrimos);
}
}
Ejecuta paso a paso y fíjate en cómo se comportan los dos bucles: por cada vuelta del bucle de fuera (numero), el bucle de dentro (divisor) arranca desde cero otra vez. Observa especialmente el numero = 12: en cuanto divisor llega a 2, 12 % 2 == 0 es verdadero, esPrimo pasa a false y el break corta el bucle de dentro al instante — no llega a probar divisor = 3. Es el mismo mecanismo que se colgó en mi primer intento, solo que aquí puedes verlo avanzar vuelta a vuelta en vez de sufrirlo en una terminal congelada.
Resumen
- Un bucle necesita siempre una forma de terminar: revisa que la variable de control cambie dentro del bucle, o te quedarás colgado como me pasó a mí
fores la opción natural cuando sabes de antemano cuántas vueltas vas a dar;whileydo-while, cuando depende de una condición que no controlas de entradado-whilees la elección correcta cuando necesitas ejecutar el cuerpo del bucle antes de poder comprobar la condición de salida — como leer un dato para saber si es el centinelabreakcorta el bucle entero;continuesolo se salta la vuelta actual y sigue con la siguiente — confundirlos es un error de intención, no de sintaxis- En bucles anidados, usa nombres de variable distintos (
i,j) para cada nivel — Java ni siquiera te deja compilar si repites el nombre - Actualiza el contador del bucle correcto: es fácil, al anidar, tocar por error la variable de fuera dentro del bucle de dentro
Chuletario — bucles en Java
// ============================================
// CHULETARIO — Bucles en Java
// Sergio Learns · sergiolearns.com
// ============================================
// FOR — cuando sabes cuántas vueltas vas a dar
for (int i = 0; i < n; i++) {
...
}
// WHILE — se repite mientras la condición sea verdadera
while (condicion) {
...
// algo dentro del bucle debe poder hacer que condicion pase a false
}
// DO-WHILE — el cuerpo se ejecuta SIEMPRE al menos una vez,
// la condición se comprueba al final
do {
...
} while (condicion);
// BREAK — corta el bucle entero de inmediato
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // sale del for, no llega a i = 6, 7, 8, 9
}
}
// CONTINUE — se salta solo la vuelta actual, el bucle sigue
for (int i = 0; i < 10; i++) {
if (i == 5) {
continue; // se salta el 5, sigue con el 6, 7, 8, 9
}
}
// BUCLES ANIDADOS — nombres de variable distintos en cada nivel
for (int i = 0; i < filas; i++) {
for (int j = 0; j < columnas; j++) {
...
}
}
// VALOR CENTINELA — repetir hasta que llegue una señal de parada
double dato;
do {
dato = sc.nextDouble();
if (dato != -1) {
// procesar dato
}
} while (dato != -1);
// RECORRER UN STRING CARÁCTER A CARÁCTER
for (int i = 0; i < texto.length(); i++) {
char c = texto.charAt(i);
...
}
// ACUMULADOR Y CONTADOR — el patrón más común dentro de un bucle
double suma = 0;
int contador = 0;
// dentro del bucle: suma += valor; contador++;
double media = suma / contador; // ← comprobar contador != 0 antes
// OPTIMIZAR UNA BÚSQUEDA DE DIVISORES
for (int d = 2; d <= Math.sqrt(numero); d++) {
if (numero % d == 0) {
break; // encontrado — no hace falta seguir
}
}
Sigue aprendiendoTodos los artículos de Java