Saltar a contenido

5. Iteración: for clásico

UD3 — Control de Flujo y Depuración · RA3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje

IES Thiar · Curso 2026 - 2027

Aspecto Valor
Resultado de aprendizaje RA: 3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje.
Criterios de evaluación a los que contribuye esta sección CE 3.2 (utiliza estructuras de repetición: for)
CE 3.5 (crea programas ejecutables combinando selección e iteración)
CE 3.6 (prueba y depura programas)
CE 3.7 (comenta y documenta el código).
Marco normativo RD 405/2023 · RD 450/2010 · RD 659/2023
Tecnología base Java 25 LTS (OpenJDK Temurin 25) — soporte hasta 2030+
Horas estimadas ~4,5 h (de las 26 h de la UD3).

Requisitos previos (lo que ya sabes): while y do-while, el concepto de precondición/poscondición, bucles infinitos, break (sección 4, con origen en la sección 3); if / else if / else, operador ternario (sección 2); switch expression y switch clásico (sección 3); métodos de String como charAt() y length() (UD2); tipos primitivos, var, operadores de asignación compuesta (UD1). Lo nuevo de esta sección es la propia sintaxis del for, sus variantes, y una primera toma de contacto —mínima e intencionadamente incompleta— con los arrays, que se estudiarán a fondo en UD6.


5.1 ¿De qué va esta sección? — Cuando ya sabes cuántas vueltas vas a dar

En la sección 4 aprendiste while y do-while: estructuras pensadas para repetir "mientras se cumpla algo", sin saber de antemano cuántas vueltas van a hacer falta. Pero hay un tipo de repetición muchísimo más frecuente en programación: repetir un número de veces que ya conoces antes de empezar. Contar del 1 al 100. Recorrer los 30 alumnos de una clase. Procesar cada carácter de una palabra. En todos estos casos no hace falta ninguna condición externa misteriosa: el número de vueltas está fijado de antemano, y solo hay que contarlas.

Para este tipo de repetición, Java —y prácticamente todos los lenguajes de programación— ofrece una estructura específica: el bucle for. No es una estructura nueva en el sentido de "hacer algo que while no pudiera hacer" —de hecho, en la sección 5.2.2 vas a comprobar que todo for se puede reescribir como un while—, sino una forma más compacta y menos propensa a errores de escribir el patrón "inicializar, comprobar, actualizar" que ya conoces de sobra.

Analogía: el monitor que cuenta las repeticiones

Cuando el monitor del gimnasio te dice "quiero que hagas exactamente 15 sentadillas", no está esperando a que "pase algo": sabe el número exacto de repeticiones antes de que empieces a hacer la primera. Eso es un for.

Cuando te dice "sigue corriendo hasta que suene el silbato", no sabe cuántas zancadas vas a dar —depende de cuándo decida hacer sonar el silbato—. Eso es un while.

Las dos formas de entrenar son legítimas y las vas a usar todo el rato; la diferencia está en si el número de repeticiones lo conoces de antemano (for) o depende de una condición externa (while / do-while).

En esta sección vas a cubrir:

  1. La anatomía del for: sus tres piezas, todas juntas en una sola línea.
  2. Las variantes: bucles decrecientes, con saltos distintos de uno en uno, con varias variables de control.
  3. El criterio de elección for frente a while.
  4. Un primer contacto —mínimo, deliberadamente incompleto— con los arrays, la estructura de datos que hace que un for con índices sea realmente útil.
  5. Cómo usar for para recorrer los caracteres de un String.
  6. Las trampas clásicas del for.
  7. Varios ejemplos resueltos paso a paso.

Abre IntelliJ, sigue en tu clase de pruebas de la sección anterior (o crea PruebasFor), y empecemos.


5.2 Anatomía del for: inicialización, condición y actualización en una sola línea

5.2.1 Sintaxis

1
2
3
for (inicializacion; condicion; actualizacion) {
    // Bloque que se repite mientras 'condicion' sea true
}

Las tres piezas que en un while estaban repartidas —una antes del bucle, una en la cabecera, una dentro del bloque— ahora viven juntas, en la misma línea, separadas por ;. Esto no es solo estética: al tenerlas todas a la vista, es mucho más difícil que se te olvide alguna, que es precisamente la causa número uno de bucle infinito que viste en la sección 4.6.

5.2.2 El for es un while "empaquetado": la equivalencia

Todo for se puede traducir, línea a línea, a un while equivalente. Compáralos:

// Un for...
for (inicializacion; condicion; actualizacion) {
    cuerpo();
}

// ...equivale exactamente a este while:
inicializacion;
while (condicion) {
    cuerpo();
    actualizacion;
}

Esta equivalencia es importante por dos razones. Primera: te confirma que no estás aprendiendo un concepto nuevo, sino una forma más compacta de escribir algo que ya dominas. Segunda: te da una herramienta de depuración —si un for "hace algo raro" y no consigues ver por qué, reescríbelo mentalmente (o en un comentario) como el while equivalente; muchas veces el error se hace visible en cuanto separas las tres piezas.

5.2.3 Primer ejemplo: contar del 1 al 5

Retomamos el primer ejemplo de la sección 4.3.3, ahora con for:

1
2
3
4
5
// Programa: cuenta del 1 al 5 usando un bucle for
for (int contador = 1; contador <= 5; contador++) {
    IO.println("Contador: " + contador);
}
IO.println("Bucle terminado.");

Salida (idéntica a la versión con while):

1
2
3
4
5
6
Contador: 1
Contador: 2
Contador: 3
Contador: 4
Contador: 5
Bucle terminado.

Compara este bloque con el de la sección 4.3.3: mismo resultado, misma lógica, pero aquí las tres piezas —int contador = 1, contador <= 5, contador++— están todas en la cabecera, en vez de repartidas en tres sitios distintos del código. Menos líneas, y ninguna posibilidad de que la inicialización o la actualización "se pierdan" en medio del cuerpo del bucle.

5.2.4 Trazado paso a paso

La tabla de trazado es exactamente igual que la de la sección 4.3.4, porque el comportamiento es idéntico —solo cambia dónde está escrita cada pieza—:

Iteración contador al empezar ¿contador <= 5? Acción realizada contador al terminar la vuelta
1 1 true Imprime "Contador: 1" 2
2 2 true Imprime "Contador: 2" 3
3 3 true Imprime "Contador: 3" 4
4 4 true Imprime "Contador: 4" 5
5 5 true Imprime "Contador: 5" 6
6 6 false No entra al bloque. Sale del for.

5.2.5 El ámbito de la variable de control

Aquí hay una diferencia real entre for y while, y esta sí importa: cuando declaras la variable de control dentro de la cabecera del for (como int contador = 1 en el ejemplo anterior), esa variable solo existe dentro del bucle. En cuanto el for termina, contador deja de existir.

1
2
3
4
for (int contador = 1; contador <= 5; contador++) {
    IO.println(contador);
}
IO.println(contador);   // ERROR DE COMPILACIÓN: cannot find symbol 'contador'

Compáralo con la versión while, donde contador se declaraba antes del bucle y seguía existiendo (y siendo consultable) después de que este terminara:

1
2
3
4
5
6
int contador = 1;
while (contador <= 5) {
    IO.println(contador);
    contador++;
}
IO.println(contador);   // Funciona perfectamente: contador vale 6 aquí

Esta diferencia de ámbito (en inglés, scope) no es un defecto del for: es una ventaja de diseño. La mayoría de las veces, la variable de control de un bucle contador (i, contador, indice...) no tiene ningún sentido fuera de él. Que Java te impida usarla por accidente fuera de su bucle es una ayuda, no un obstáculo: evita que reutilices sin querer una variable "sobrante" en otra parte del código.

5.2.6 Diagrama de flujo

flowchart TD
    Inicio([Inicio]) --> Init["contador = 1"]
    Init --> Condicion{"contador <= 5 ?"}
    Condicion -->|Sí| Bloque["Imprime contador"]
    Bloque --> Actualiza["contador++"]
    Actualiza --> Condicion
    Condicion -->|No| Fin([Fin])

Es el mismo esqueleto que el diagrama del while de la sección 4.3.6, con un matiz: aquí la actualización aparece como un paso explícito del diagrama, porque en el for está declarada de forma independiente en la cabecera, aunque conceptualmente pertenece "al final de cada vuelta", igual que en el while.


5.3 Variantes del for

5.3.1 Bucle decreciente

Nada obliga a que la variable de control aumente. Puedes hacer una cuenta atrás cambiando la inicialización, la condición y la actualización:

1
2
3
4
5
// Cuenta atrás de 10 a 1
for (int i = 10; i >= 1; i--) {
    IO.println(i);
}
IO.println("¡Despegue!");

Fíjate en que las tres piezas cambian de forma coherente entre sí: empiezas alto (i = 10), la condición comprueba que no hayas bajado del límite inferior (i >= 1), y la actualización decrece (i--). Si solo cambias una de las tres piezas y olvidas las otras dos, el bucle no hace lo que quieres —o, peor, se convierte en infinito—.

5.3.2 Saltos distintos de uno en uno

La actualización no tiene por qué ser i++ o i--; puede ser cualquier expresión que modifique la variable de control:

1
2
3
4
// Números pares del 0 al 20
for (int i = 0; i <= 20; i += 2) {
    IO.println(i);
}
1
2
3
4
// Cuenta atrás de 100 a 0, de 10 en 10
for (int i = 100; i >= 0; i -= 10) {
    IO.println(i);
}

5.3.3 Múltiples variables de control

Java permite declarar y actualizar más de una variable en la misma cabecera, separándolas por comas (aunque solo se admite una condición de continuación):

1
2
3
4
// Dos variables de control moviéndose en direcciones opuestas
for (int i = 0, j = 10; i < j; i++, j--) {
    IO.println("i = " + i + ", j = " + j);
}

Salida:

1
2
3
4
5
i = 0, j = 10
i = 1, j = 9
i = 2, j = 8
i = 3, j = 7
i = 4, j = 6

El bucle termina en cuanto i < j deja de cumplirse (cuando i vale 5 y j vale 5, 5 < 5 es false). Este patrón es poco frecuente en código sencillo, pero lo verás en algunos algoritmos —por ejemplo, para comprobar si una palabra es un palíndromo recorriendo desde los dos extremos hacia el centro a la vez—.

5.3.4 Partes opcionales: el for "vacío"

Las tres partes de la cabecera de un for son, técnicamente, opcionales. Si las omites todas, obtienes un bucle infinito equivalente a while (true):

1
2
3
4
// Equivalente a while (true)
for (;;) {
    IO.println("Esto se repite para siempre, hasta un break");
}

No es un truco oscuro: te lo vas a encontrar en código de otras personas, sobre todo en código más antiguo o de estilo C. Sin embargo, en Java moderno se prefiere while (true) para este caso, precisamente porque comunica la intención con más claridad —un for vacío obliga a pararse a pensar "¿por qué no hay ninguna de las tres partes?", mientras que while (true) se lee de un vistazo—. Guarda for (;;) como algo que sabes reconocer, no como tu forma habitual de escribir un bucle infinito.


5.4 for frente a while: criterio de elección

5.4.1 Tabla comparativa

Situación Estructura recomendada
Conoces de antemano exactamente cuántas veces hay que repetir (contar del 1 al 10, recorrer los 30 elementos de un array). for
No sabes cuántas veces se repetirá; depende de una condición externa que solo se conoce en tiempo de ejecución (validar un dato, esperar a que el usuario escriba "fin"). while
La acción tiene que ejecutarse siempre al menos una vez, sin excepción (menús, pedir un dato por primera vez). do-while

5.4.2 Regla del profesor

Si en tu cabeza puedes terminar la frase "repite exactamente N veces", con un número (o una expresión) concreto para N, usa for.

Si la frase que te sale es "repite mientras...", sin un número claro de antemano, usa while (o do-while, si además tiene que ejecutarse sí o sí la primera vez).

Igual que con la elección entre while y do-while en la sección 4.5.4, esto no es una norma arbitraria de estilo: es un reflejo de lo que cada estructura comunica mejor al leer el código. Las tres estructuras pueden, técnicamente, resolver el mismo problema —lo demuestra la equivalencia forwhile de la sección 5.2.2—, pero elegir la que mejor expresa tu intención hace que tu código sea más fácil de leer, de depurar y de mantener por otra persona (o por ti mismo, dentro de seis meses).


5.5 Primer contacto con los arrays: la pieza que le faltaba al for

5.5.1 Aviso pedagógico: introducción mínima, profundización en UD6

Vas a usar arrays antes de estudiarlos a fondo

La razón de ser del for con índices —recorrer una colección de datos numerada, elemento a elemento— solo se aprecia de verdad cuando tienes una colección de datos que recorrer. Por eso en esta sección vas a conocer lo mínimo imprescindible sobre los arrays: declararlos, inicializarlos, acceder a sus elementos por índice y consultar su tamaño.

El tratamiento completo —arrays multidimensionales, arrays dentados, la clase Arrays con sus métodos de ordenación y búsqueda— llega en la UD6, sección 1, donde se evalúa formalmente con el criterio CE 6.1 ("Se han escrito programas que utilicen matrices (arrays)"), perteneciente al RA6. Lo que aprendes aquí es una herramienta práctica para dar sentido al for, no una sustitución de esa profundización posterior: no te preocupes si por ahora solo sabes lo básico.

5.5.2 ¿Qué es un array?

Un array (también llamado matriz o arreglo) es una colección de valores del mismo tipo, con un tamaño fijo decidido en el momento de crearlo, a los que se accede mediante un índice numérico que empieza en 0.

Piensa en un array como una fila de casilleros numerados, todos del mismo tamaño y pensados para guardar el mismo tipo de objeto: el casillero número 0 es el primero, no el "casillero cero" en el sentido coloquial.

5.5.3 Declaración e inicialización

1
2
3
4
5
6
7
8
// Declaración con tamaño fijo: 5 huecos para enteros, todos inicializados a 0
int[] numeros = new int[5];

// Declaración con valores iniciales conocidos
int[] edades = {18, 22, 19, 25, 20};

// Forma equivalente, más explícita
int[] otrasEdades = new int[]{18, 22, 19, 25, 20};

Cuando creas un array con new int[5] sin darle valores, Java lo rellena con el valor por defecto del tipo —0 para tipos numéricos, false para boolean, null para tipos de referencia como String—, el mismo concepto de "valores por defecto de campos" que ya viste en UD1.

5.5.4 Acceso por índice y la propiedad length

1
2
3
4
5
int[] edades = {18, 22, 19, 25, 20};

IO.println(edades[0]);   // 18 (primer elemento)
IO.println(edades[4]);   // 20 (último elemento, porque hay 5 elementos: índices 0 a 4)
IO.println(edades.length);   // 5 (número total de elementos)

Trampa que viene: length es un campo, no un método

Fíjate bien: edades.length no lleva paréntesis. Es un campo del array, no un método. Esto contrasta directamente con lo que ya sabes de String: palabra.length() lleva paréntesis, porque ahí es un método. Confundir ambos —escribir array.length() o cadena.length— es un error de compilación tan común que merece su propio apartado en la sección de trampas (5.7).

5.5.5 Recorrer un array con for

Aquí es donde todo encaja: el for es la herramienta natural para visitar, uno por uno, todos los índices válidos de un array.

1
2
3
4
5
int[] edades = {18, 22, 19, 25, 20};

for (int i = 0; i < edades.length; i++) {
    IO.println("edades[" + i + "] = " + edades[i]);
}

Salida:

1
2
3
4
5
edades[0] = 18
edades[1] = 22
edades[2] = 19
edades[3] = 25
edades[4] = 20

Fíjate en la condición: i < edades.length, no i <= edades.length. Como los índices válidos van de 0 a length - 1, la condición de continuación tiene que ser estrictamente "menor que" la longitud, no "menor o igual que". Usar edades.length en la condición, en vez de escribir el número 5 directamente, tiene una ventaja añadida: si mañana el array tiene 8 elementos en vez de 5, el bucle se adapta automáticamente, sin que tengas que tocar ni una línea del for.

5.5.6 Cuidado: ArrayIndexOutOfBoundsException

Si accedes a un índice que no existe —negativo, o mayor o igual que length—, el programa lanza una excepción y se detiene:

1
2
3
int[] edades = {18, 22, 19};   // índices válidos: 0, 1, 2

IO.println(edades[3]);   // ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3

No te preocupes todavía por capturar esta excepción con try-catch —eso llega en la sección 8—; por ahora, basta con que sepas reconocerla, entender por qué ocurre (un índice fuera del rango [0, length - 1]), y comprobar siempre que tu condición de bucle usa < y no <= cuando recorres un array completo.


5.6 for sobre índices de String

5.6.1 Recorrer caracteres con charAt() y length()

Ya conoces String.charAt(int indice) y String.length() desde UD2. Con for, puedes recorrer una cadena carácter a carácter exactamente con el mismo patrón que usaste para un array, cambiando solo array[i] por cadena.charAt(i) y array.length por cadena.length():

1
2
3
4
5
6
String palabra = "programacion";

for (int i = 0; i < palabra.length(); i++) {
    char c = palabra.charAt(i);
    IO.println("Carácter " + i + ": " + c);
}

Recordatorio de la trampa anterior, al revés

Aquí palabra.length() lleva paréntesis, porque en String es un método. Si vienes de trabajar con un array y escribes palabra.length (sin paréntesis) por costumbre, no compilará. Memorizar esta asimetría —campo en array, método en String— te va a ahorrar más de un error de compilación en las próximas semanas.

5.6.2 Ejemplo: contar vocales

String palabra = "Programacion";
palabra = palabra.toLowerCase();   // normalizamos a minúsculas antes de comparar
int vocales = 0;

for (int i = 0; i < palabra.length(); i++) {
    char c = palabra.charAt(i);
    if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
        vocales++;
    }
}

IO.println("La palabra \"" + palabra + "\" tiene " + vocales + " vocales.");

5.6.3 Ejemplo: comprobar si una palabra es un palíndromo

Un palíndromo es una palabra que se lee igual del derecho que del revés (por ejemplo, "reconocer" o "ana"). Puedes comprobarlo comparando el primer carácter con el último, el segundo con el penúltimo, y así hasta llegar al centro:

String palabra = "reconocer";
palabra = palabra.toLowerCase();
boolean esPalindromo = true;

for (int i = 0; i < palabra.length() / 2; i++) {
    char inicio = palabra.charAt(i);
    char fin = palabra.charAt(palabra.length() - 1 - i);

    if (inicio != fin) {
        esPalindromo = false;
        break;   // ya tenemos la respuesta, no hace falta seguir comparando
    }
}

IO.println(esPalindromo ? "\"" + palabra + "\" es un palíndromo." : "\"" + palabra + "\" no es un palíndromo.");

Puntos a observar:

  1. palabra.length() / 2 como límite: solo hace falta comparar hasta la mitad de la palabra; comparar más allá del centro repetiría las mismas comparaciones al revés.
  2. palabra.length() - 1 - i calcula el índice "simétrico" al i actual, contando desde el final.
  3. break (ya conocido de las secciones 3 y 4) corta el bucle en cuanto se encuentra una diferencia, sin desperdiciar comparaciones innecesarias: es un ejemplo perfecto de por qué break es útil dentro de un for, no solo dentro de un while (true).
  4. Operador ternario (sección 2) en el IO.println final, para elegir el mensaje según el resultado.

5.7 Trampas clásicas del for

5.7.1 Modificar la variable de control dentro del cuerpo

Si además de la actualización automática de la cabecera modificas la variable de control dentro del bloque, el resultado casi nunca es el esperado:

1
2
3
4
5
// BUG: 'i' se modifica dos veces por vuelta (una en la cabecera, otra en el cuerpo)
for (int i = 0; i < 10; i++) {
    IO.println(i);
    i++;   // salta números: se imprimen 0, 2, 4, 6, 8, y el bucle termina antes de lo esperado
}

El for ya se encarga de actualizar i por ti en la cabecera; tocarla también dentro del cuerpo duplica el efecto y hace que el número real de vueltas sea distinto del que "se lee" en la cabecera. Si necesitas saltar elementos, cambia la actualización de la cabecera (i += 2, como en la sección 5.3.2), no añadas una segunda modificación dentro del bloque.

5.7.2 Off-by-one con length

La trampa más frecuente al recorrer arrays o cadenas con for:

1
2
3
4
5
int[] numeros = {10, 20, 30};

for (int i = 0; i <= numeros.length; i++) {   // BUG: <= en vez de <
    IO.println(numeros[i]);   // ArrayIndexOutOfBoundsException en la última vuelta (i = 3)
}

Como los índices válidos van de 0 a length - 1, la condición correcta es i < numeros.length. Usar <= hace que la última vuelta intente acceder al índice numeros.length (en este ejemplo, 3), que no existe, y el programa se detiene con ArrayIndexOutOfBoundsException. Es exactamente el mismo tipo de error que viste en la sección 4.10.4 con while, pero aquí sus consecuencias son más graves: no solo "un número de más o de menos" en la salida, sino una excepción que detiene el programa.

5.7.3 El ; fantasma también existe en el for

Igual que en el while (sección 4.10.1), un ; de más justo después de la cabecera del for crea un cuerpo vacío:

1
2
3
4
5
// BUG: el ; convierte el cuerpo del for en una sentencia vacía
for (int i = 0; i < 5; i++);
{
    IO.println("Esto se ejecuta una vez, después del for, no dentro de él");
}

Aquí el for sí termina —a diferencia del caso equivalente con while, la actualización i++ está en la cabecera y ocurre igualmente aunque el cuerpo esté vacío—, pero no hace nada útil: el bloque { } que viene después no pertenece al for, es un bloque de código suelto que se ejecuta una sola vez, después de que el bucle termine. El síntoma no es un bucle infinito, sino "el bucle no imprime nada de lo que esperaba".

5.7.4 Reutilizar el nombre de la variable de control en bucles anidados

Cuando anides un for dentro de otro —contenido formal de la sección 7—, no puedes reutilizar el mismo nombre de variable de control en ambos si uno está directamente dentro del otro:

1
2
3
4
5
6
// ERROR DE COMPILACIÓN: 'i' ya está definida en el ámbito exterior
for (int i = 0; i < 5; i++) {
    for (int i = 0; i < 3; i++) {   // no compila
        // ...
    }
}

La solución es usar nombres distintos para cada nivel de anidamiento —por convención, i para el bucle exterior, j para el interior, k para un tercer nivel—:

1
2
3
4
5
6
// CORRECTO: nombres distintos para cada nivel
for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 3; j++) {
        IO.println("i = " + i + ", j = " + j);
    }
}

No profundizamos más en bucles anidados aquí —eso es contenido de la sección 7—, pero conviene que fijes ya esta convención de nombres, porque la vas a usar constantemente en cuanto empieces a anidar bucles.

5.7.5 Resumen de trampas

Trampa Síntoma Solución
Modificar la variable de control dentro del cuerpo, además de en la cabecera El bucle avanza más rápido de lo esperado, saltando valores. Modifica la variable de control únicamente en la cabecera del for.
Off-by-one con length (<= en vez de <) ArrayIndexOutOfBoundsException en la última vuelta. Usa siempre i < array.length (o i < cadena.length()) al recorrer por completo.
; de más tras la cabecera del for El bloque previsto no se ejecuta ninguna vez. Revisa que no haya ; justo después del paréntesis de cierre.
Reutilizar el nombre de la variable de control en bucles anidados directos Error de compilación: variable ya definida. Usa nombres distintos (i, j, k) en cada nivel de anidamiento.
Confundir array.length (campo) con cadena.length() (método) Error de compilación por paréntesis de más o de menos. Recuerda: en arrays es un campo, en String es un método.

5.8 Ejemplos resueltos paso a paso

5.8.1 Ejemplo 1: suma de los primeros N números naturales

import java.util.Scanner;

public class SumaPrimerosN {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        IO.print("¿Hasta qué número sumamos? ");
        int n = Integer.parseInt(sc.nextLine().trim());

        int suma = 0;
        for (int i = 1; i <= n; i++) {
            suma += i;
        }

        IO.println("La suma de 1 a " + n + " es " + suma);

        sc.close();
    }
}

Puntos a observar: si el usuario introduce 0 o un número negativo, la condición i <= n es false desde el principio (con i empezando en 1), así que el bucle no se ejecuta ninguna vez y suma se queda en 0 — el mismo comportamiento de "cero vueltas" que viste con while en la sección 4.3.5, heredado aquí a través de la equivalencia forwhile.

5.8.2 Ejemplo 2: factorial de un número

import java.util.Scanner;

public class Factorial {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        IO.print("Calcular el factorial de: ");
        int n = Integer.parseInt(sc.nextLine().trim());

        long factorial = 1;   // 'long' para admitir resultados grandes sin desbordar
        for (int i = 2; i <= n; i++) {
            factorial *= i;
        }

        IO.println(n + "! = " + factorial);

        sc.close();
    }
}

Puntos a observar:

  1. long en vez de int para factorial: los factoriales crecen muy rápido (13! ya supera el máximo de int), así que se usa el tipo de 64 bits visto en UD1.
  2. El bucle empieza en i = 2, no en i = 1: multiplicar por 1 no cambia nada, así que empezar en 2 ahorra una vuelta inútil sin afectar al resultado.
  3. Si el usuario introduce 0 o 1, la condición i <= n es false desde el principio (2 <= 0 y 2 <= 1 son ambas falsas), el bucle no se ejecuta, y factorial se queda en su valor inicial 1 — que es, precisamente, el resultado matemático correcto de 0! y 1!. Una propiedad del for que resuelve un caso especial sin necesidad de ningún if adicional.

5.8.3 Ejemplo 3: notas de una clase (array, media y nota máxima)

import java.util.Scanner;

public class NotasClase {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        sc.useLocale(java.util.Locale.US);

        IO.print("¿Cuántos alumnos hay? ");
        int numAlumnos = Integer.parseInt(sc.nextLine().trim());

        double[] notas = new double[numAlumnos];

        for (int i = 0; i < notas.length; i++) {
            IO.print("Nota del alumno " + (i + 1) + ": ");
            notas[i] = Double.parseDouble(sc.nextLine().trim().replace(',', '.'));
        }

        double suma = 0;
        double notaMaxima = notas[0];

        for (int i = 0; i < notas.length; i++) {
            suma += notas[i];
            if (notas[i] > notaMaxima) {
                notaMaxima = notas[i];
            }
        }

        double media = suma / notas.length;

        IO.println(String.format("Media de la clase: %.2f", media));
        IO.println(String.format("Nota máxima: %.2f", notaMaxima));

        sc.close();
    }
}

Puntos a observar:

  1. Dos bucles for independientes, sobre el mismo array: el primero rellena el array pidiendo datos por teclado; el segundo lo recorre para calcular estadísticas. No se pueden fusionar en uno solo porque, para saber cuál es la nota máxima, necesitas comparar cada nota con las anteriores, y eso requiere que todas ya estén guardadas.
  2. notaMaxima = notas[0] como valor inicial: empezar comparando contra el primer elemento del propio array (no contra un 0 arbitrario) evita errores si todas las notas fueran, por ejemplo, negativas (aunque aquí no debería ocurrir, es una buena costumbre general).
  3. (i + 1) en el mensaje al usuario: los índices del array empiezan en 0, pero mostrarle "Alumno 0" al usuario sería confuso; se suma 1 solo para la parte visual, sin que eso afecte al índice real usado en notas[i].
  4. Reutiliza el patrón de Scanner con useLocale y .replace(',', '.') ya visto en la sección 4.11.4 para admitir tanto coma como punto decimal.

5.8.4 Ejemplo 4: contador de vocales en una palabra (versión completa)

import java.util.Scanner;

public class ContadorVocales {

    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);

        IO.print("Escribe una palabra: ");
        String palabra = sc.nextLine().trim().toLowerCase();

        int vocales = 0;
        for (int i = 0; i < palabra.length(); i++) {
            char c = palabra.charAt(i);
            if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
                vocales++;
            }
        }

        IO.println("La palabra \"" + palabra + "\" tiene " + vocales
                + " vocal" + (vocales == 1 ? "" : "es") + ".");

        sc.close();
    }
}

Puntos a observar: el pequeño operador ternario final (vocales == 1 ? "" : "es") es un detalle de pulido —singular frente a plural en el mensaje— que demuestra cómo el operador ternario de la sección 2 sigue siendo útil en contextos completamente distintos, dentro de una concatenación de String.


5.9 Buenas prácticas y reglas de oro

5.9.1 Las 7 reglas del for profesional

  1. Usa for cuando conozcas de antemano el número de repeticiones; si depende de una condición externa, usa while o do-while.
  2. Declara la variable de control dentro de la cabecera del for si no la necesitas fuera de él: aprovecha el ámbito limitado como protección, no como limitación.
  3. No modifiques la variable de control dentro del cuerpo además de en la cabecera.
  4. Usa array.length o cadena.length() en la condición, nunca un número fijo, para que el bucle se adapte automáticamente al tamaño real de los datos.
  5. Recuerda la asimetría: length es un campo en los arrays (sin paréntesis), length() es un método en String (con paréntesis).
  6. En bucles anidados, usa nombres de variable distintos (i, j, k), tanto por obligación del compilador como por claridad.
  7. Evita for (;;) salvo que sea una convención explícita de tu equipo; while (true) comunica la misma idea con más claridad.

5.9.2 Cuándo usar cada bucle: tabla definitiva

Con while, do-while y for ya en tu caja de herramientas, esta es la tabla de referencia que deberías tener siempre presente antes de escribir cualquier repetición:

Pregunta que te haces Estructura
"¿Sé exactamente cuántas veces se va a repetir esto?" for
"¿No lo sé, depende de una condición que solo conoceré en tiempo de ejecución?" while
"¿Tiene que ejecutarse siempre, al menos una vez, pase lo que pase?" do-while
"¿La condición de parada solo se conoce a mitad del bloque (patrón loop-and-a-half)?" while (true) + break

5.10 Resumen y mapa conceptual

5.10.1 Mapa conceptual

mindmap
  root((Sección 5 - for clásico))
    Anatomía
      Inicialización condición actualización
      Equivalente a un while empaquetado
      Ámbito limitado de la variable de control
    Variantes
      Bucle decreciente
      Saltos distintos de uno en uno
      Múltiples variables de control
      for vacío for punto y coma punto y coma
    Arrays introducción mínima
      Declaración e inicialización
      Acceso por índice desde 0
      length es un campo sin paréntesis
      ArrayIndexOutOfBoundsException
    for sobre Strings
      charAt y length con paréntesis
      Contar vocales
      Comprobar palíndromos
    Trampas
      Modificar la variable dos veces
      Off-by-one con length
      Punto y coma fantasma
      Reutilizar nombre en bucles anidados

5.10.2 Las 10 ideas que no debes olvidar

  1. El for reúne inicialización, condición y actualización en una sola línea; es un while reorganizado, no una estructura distinta.
  2. La variable de control declarada en la cabecera del for solo existe dentro del bucle.
  3. Usa for cuando conozcas de antemano el número de repeticiones; while/do-while en caso contrario.
  4. La actualización no tiene por qué ser ++: puede decrecer, saltar de más de uno en uno, o incluso controlar varias variables a la vez.
  5. for (;;) existe y equivale a while (true), pero while (true) suele ser más legible.
  6. Un array tiene tamaño fijo, índices desde 0, y todos sus elementos son del mismo tipo.
  7. array.length es un campo (sin paréntesis); cadena.length() es un método (con paréntesis). No los confundas.
  8. La condición correcta para recorrer un array o cadena completos es i < length, nunca i <= length.
  9. ArrayIndexOutOfBoundsException aparece al acceder a un índice negativo o mayor o igual que length.
  10. En bucles anidados, cada nivel necesita su propio nombre de variable de control (i, j, k).

5.10.3 Glosario rápido

Término Significado
for Bucle controlado por contador, con inicialización, condición y actualización reunidas en la cabecera.
Ámbito (scope) Región del código en la que una variable declarada es visible y utilizable.
Array Colección de tamaño fijo de valores del mismo tipo, accesibles por índice numérico desde 0.
Índice Posición numérica de un elemento dentro de un array o de un carácter dentro de una cadena.
length Campo (en arrays) o método (en String) que indica el número de elementos o caracteres.
ArrayIndexOutOfBoundsException Excepción lanzada al acceder a un índice fuera del rango válido [0, length - 1] de un array.
Palíndromo Palabra o frase que se lee igual del derecho que del revés.
Off-by-one Error de lógica en el que un bucle se ejecuta una vez de más o de menos, típicamente por confundir < y <=.
Bucle anidado Un bucle dentro de otro (tratamiento formal en la sección 7).

5.11 Actividades propuestas

Cinco actividades graduadas, en orden de dificultad creciente.

5.11.1 Actividad 1 — Serie de Fibonacci

Objetivo: practicar el for con múltiples variables acumuladoras.

Enunciado: Escribe un programa SerieFibonacci que pida un número n y muestre los primeros n términos de la sucesión de Fibonacci (donde cada término es la suma de los dos anteriores: 0, 1, 1, 2, 3, 5, 8, 13...).

Requisitos:

  • Usa un for con contador conocido (i de 1 a n), no while.
  • Usa dos variables (long anterior y long actual, por ejemplo) para llevar los dos últimos términos, actualizándolas dentro del bucle.
  • Valida que n sea mayor que 0 antes de empezar (puedes reutilizar el patrón de validación de la sección 4.7).

Entrega: archivo SerieFibonacci.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.2, CE 3.5, CE 3.7.

5.11.2 Actividad 2 — Estadísticas de temperaturas

Objetivo: practicar arrays combinados con for, ampliando el ejemplo 5.8.3.

Enunciado: Escribe un programa EstadisticasTemperaturas que pida cuántas mediciones de temperatura se van a introducir, las almacene en un array de double, y muestre:

  • La temperatura media.
  • La temperatura máxima y en qué posición (medición número X) se registró.
  • La temperatura mínima y en qué posición se registró.
  • Cuántas mediciones fueron negativas (bajo cero).

Requisitos:

  • Usa al menos dos bucles for: uno para rellenar el array, otro (o varios) para procesarlo.
  • Usa array.length en las condiciones, nunca un número fijo.
  • Admite tanto , como . como separador decimal.

Entrega: archivo EstadisticasTemperaturas.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.2, CE 3.5, CE 3.6, CE 3.7.

5.11.3 Actividad 3 — Comprobador de números primos

Objetivo: practicar for combinado con break para una salida anticipada.

Enunciado: Escribe un programa ComprobadorPrimo que pida un número entero mayor que 1 y determine si es primo (un número primo solo es divisible por 1 y por sí mismo).

Requisitos:

  • Recorre con un for los posibles divisores desde 2 hasta la raíz cuadrada del número (Math.sqrt, ya visto en UD2), no hasta el número completo: es mucho más eficiente.
  • Usa una variable booleana esPrimo, inicializada a true, que se ponga a false en cuanto se encuentre un divisor.
  • Usa break para salir del bucle en cuanto se encuentre un divisor, sin seguir comprobando innecesariamente (como en el ejemplo del palíndromo, sección 5.6.3).

Entrega: archivo ComprobadorPrimo.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.2, CE 3.5, CE 3.7.

5.11.4 Actividad 4 — Analizador de contraseñas

Objetivo: practicar for sobre String, clasificando caracteres.

Enunciado: Escribe un programa AnalizadorContrasena que pida una contraseña y, recorriéndola carácter a carácter con un for, cuente e informe de:

  • Cuántas letras mayúsculas contiene.
  • Cuántas letras minúsculas contiene.
  • Cuántos dígitos contiene.
  • Cuántos caracteres "especiales" contiene (cualquier cosa que no sea letra ni dígito).

Al final, si la contraseña tiene al menos 8 caracteres, al menos una mayúscula, al menos un dígito y al menos un carácter especial, muestra "Contraseña fuerte"; en caso contrario, "Contraseña débil".

Requisitos:

  • Usa Character.isUpperCase(c), Character.isLowerCase(c) y Character.isDigit(c) (clase Character, ya usada de forma puntual en UD2) dentro del for.
  • No uses ninguna estructura de repetición distinta de for.

Entrega: archivo AnalizadorContrasena.java en el paquete jams.programacion.ud3.

Criterios evaluados: CE 3.2, CE 3.5, CE 3.7.

5.11.5 Actividad 5 — Cuestionario de repaso

Responde por escrito:

  1. ¿Qué tres piezas componen la cabecera de un for, y en qué orden van?
  2. Demuestra, escribiendo el while equivalente, que for (int i = 0; i < 5; i++) { accion(); } no es una estructura "mágica" distinta de lo que ya sabías.
  3. ¿Por qué no puedes usar, después de un for, la variable de control que declaraste en su cabecera?
  4. ¿Cuál es la diferencia entre array.length y cadena.length()? ¿Por qué existe esa diferencia?
  5. Explica con tus palabras por qué la condición correcta para recorrer un array completo es i < array.length y no i <= array.length.
  6. ¿Qué excepción se lanza si accedes a un índice de array fuera de rango? ¿Cuáles son los índices válidos de un array de longitud 7?
  7. ¿Cuándo elegirías for en vez de while? Pon un ejemplo distinto a los vistos en la sección.
  8. ¿Qué problema provoca modificar la variable de control tanto en la cabecera del for como dentro de su cuerpo?
  9. Escribe la cabecera de un for que recorra los números pares del 100 al 0, en orden descendente.
  10. ¿Por qué en bucles anidados no puedes usar el mismo nombre de variable de control en ambos niveles si uno está directamente dentro del otro?

Entrega: archivo cuestionario_seccion5.md con tus respuestas.

Criterios evaluados: CE 3.2, CE 3.7.


5.12 Lo que viene en la sección 6

Ya dominas las tres estructuras de repetición "clásicas" de Java: while, do-while y for, sabes elegir entre ellas según conozcas o no de antemano el número de repeticiones, y has dado tus primeros pasos con arrays —lo justo para recorrerlos con índices—. En la sección 6 vas a conocer una cuarta forma de repetir, pensada específicamente para recorrer colecciones de datos sin tener que preocuparte del índice: el for-each (o enhanced for).

Aprenderás:

  • La sintaxis for (Tipo elemento : coleccion), mucho más compacta que el for con índices cuando no necesitas la posición de cada elemento.
  • Que funciona no solo con arrays, sino con cualquier colección que implemente Iterable<T> (algo que profundizarás en UD6).
  • Su limitación más importante: no puedes modificar la colección mientras la recorres con for-each.
  • El criterio para saber cuándo todavía necesitas el índice —y por tanto, el for clásico de esta sección— y cuándo el for-each es la opción más limpia.

El for-each no sustituye al for clásico: lo complementa. Vas a seguir usando el for con índices cada vez que necesites saber "en qué posición estoy", como en los ejemplos de palíndromos o de nota máxima de esta sección; el for-each será tu opción por defecto cuando solo te importe "cada elemento", sin preocuparte de su posición.


Cierre del profesor. Si la sección 4 te dio la capacidad de repetir "hasta que pase algo", esta sección 5 te da la capacidad de repetir "exactamente las veces que yo decida". Son dos herramientas distintas para dos preguntas distintas, y ahora sabes reconocer cuál te está haciendo cada problema antes de escribir la primera línea de código. De regalo, esta sección te ha dado además tu primer array: una fila de casilleros numerados que, combinada con el for, te permite procesar datos de verdad —notas de una clase, temperaturas, contraseñas— en vez de valores sueltos. Es una introducción deliberadamente pequeña; en UD6 vas a volver sobre los arrays con mucha más profundidad, pero ya tienes lo esencial para no sentirte perdido cuando eso ocurra. Nos vemos en la sección 6, donde vas a descubrir que, para muchos de los for con índices que acabas de escribir, existe una forma todavía más corta de decir lo mismo.