Tabla de Contenidos

05 - Programación estructurada

La programación estructurada es un paradigma de programación donde un programa utiliza solo 3 estructuras básicas: secuencias, selecciones (condicionales) e iteraciones (bucles).

Por ahora hemos estado utilizando solo secuencias, donde una instrucción se ejecutaba tras otra. En este tema vamos a ver las otras dos estructuras básicas.

Selección

Se ejecuta un conjunto de instrucciones en función del resultado de evaluar una condición (true o false). En Java existen dos estructuras básicas de selección: if-else y switch.

if-else

La sentencia if contiene una condición (o conjunto de condiciones) que se evalúa a true o false. En función del resultado se ejecutan o no las sentencias encerradas en el bloque de código siguiente:

class SentenciaIf {
    
    public static void main(String[] args) {
        int number = 3;

        if(number<5) {
            System.out.println("El número es menor que 5");
        }
        System.out.println("Fin del programa");
    }
}

El número es menor que 5
Fin del programa

En el ejemplo anterior, definimos una variable de tipo entero con valor 3. A continuación, usamos la sentencia if para preguntar si el número es menor que 5. Como en este caso se cumple la condición, se ejecuta el bloque de código encerrado entre llaves y muestra la frase El número es menor que 5. Si modificamos el valor de la variable con un número mayor que 5 y volvemos a ejecutar el programa, vemos que ya no muestra la frase (no se ejecutan las líneas de código de la sentencia if).

...
int number = 7;
...

Fin del programa

A la hora de evaluar una condición, hay que poner especial atención con los límites. Por ejemplo, en el código anterior, si la variable es igual a 5 no se mostraría la frase El número es menor que 5, ya que estamos preguntando si la variable es menor que 5. Si quisiésemos que se ejecutara el código cuando la variable es menor o igual a 5 deberíamos modificar el código:
...
if(number<=5) {
...
Muchos problemas típicos (sobre todo cuando se está aprendiendo a programar) vienen de no evaluar la condición adecuada.

En el ejemplo anterior hemos evaluado una condición simple, pero podemos evaluar condiciones múltiples usando los operadores && (AND) y || (OR):

if(number<5 && number>2)
{
    System.out.println("El número es menor que 5 y mayor que 2");
}

Las condiciones pueden ser todo lo complicadas que queramos, aunque, en general, siempre es mejor usar condiciones lo más simples posible para mejorar la legibilidad de nuestro código (por ejemplo usando funciones, como veremos más adelante).

¿Y si en nuestro primer ejemplo queremos que cuando el número sea mayor o igual a 5 se muestra la frase El número es mayor o igual a 5? Podríamos utilizar otra sentencia if a continuación de la primera para evaluar esa condición y mostrar la segunda frase:

if(number<5) 
{
    System.out.println("El número es menor que 5");
}
if(number >= 5)
{
    System.out.println("El número es mayor o igual que 5");
}

Si ejecutamos la aplicación cambiando el valor de la variables (por ejemplo, primero con number=3 y después con number=8) vemos que funciona como toca. El problema es que no es todo lo eficiente que debería ser. En este caso, estamos evaluando dos veces la misma condición (o similar), con el gasto computacional que implica. Para estos casos, existe la sentencia else. Esta sentencia se utiliza después de un if para ejecutar un bloque de código en caso que la condición del if sea evaluada como false:

if(number<5) 
{
    System.out.println("El número es menor que 5");
} else {
    System.out.println("El número es mayor o igual que 5");
}

Que un programa funcione no quiere decir que sea eficiente. En el ejemplo anterior no tiene mucha importancia, ya que es una aplicación con muy pocas líneas de código y muy sencillas, pero cuando hacemos un programa con miles de líneas de código, la eficiencia es un parámetro muy importante a tener en cuenta.

Podemos encadenar varias sentencias if-else para evaluar múltiples condiciones:

if(number<5)
{
    System.out.println("El número es menor que 5");
} else if (number < 8) {
    System.out.println("El número está en el rango [5, 8[");
} else if (number <10){
    System.out.println("El número está en el rango [8, 10[");
} else {
    System.out.println("El número es mayor que 10");
}

Aunque el código anterior es perfectamente válido, la mayoría de veces nos conviene más utilizar (sobre todo por legibilidad y sencillez del código) la sentencia que veremos a continuación: switch.

switch

La sentencia switch es equivalente a encadenar varias sentencias if-else, pero de forma más elegante. Su sintaxis clásica (antes de Java 17) es:

        switch (key) {
            case value:
                
                break;
        
            default:
                break;
        }

La variable key es lo que vamos a evaluar. A continuación, vamos añadiendo bloques case value que comprobaran si key tiene ese valor. En caso afirmativo, se ejecutará el código de ese bloque y terminará la ejecución de la sentencia. Si key no tiene ningún valor de los anteriores, se ejecutará el código del bloque default (este bloque no es obligatorio, aunque normalmente está presente en la sentencia):

Por ejemplo, podemos sustituir el siguiente código:

        if (number == 5) {
            System.out.println("El número es igual a 5");
        } else if(number == 6){
            System.out.println("El número es igual a 6");
        } else if(number == 7) {
            System.out.println("El número es igual a 7");
        } else {
            System.out.println("El número es mayor que 7");
        }

usando la sentencia switch:

        switch (number) {
            case 5:
                System.out.println("El número es igual a 5");
                break;
            case 6:
                System.out.println("El número es igual a 6");
                break;
            case 7:
                System.out.println("El número es igual a 7");
                break;
            default:
                break;
        }
    }

La sentencia break le indica al intérprete que debe salir del bloque switch, con lo que no debe ejecutar las siguientes líneas. Para entenderlo, elimina las sentencias break del código anterior y ejecútalo:

        switch (number) {
            case 5:
                System.out.println("El número es igual a 5");
            case 6:
                System.out.println("El número es igual a 6");
            case 7:
                System.out.println("El número es igual a 7");
        }

El número es igual a 5
El número es igual a 6
El número es igual a 7

Como ves, al no existir la sentencia break el código seguiría ejecutándose y mostraría todas las frases por pantalla.

¿Qué pasa si queremos que se ejecute el mismo código cuando nuestra variable sea igual a un conjunto de valores? Para ese caso, podemos aprovechar lo que hemos visto de la ausencia de sentencia break y agrupar varios valores:

        switch (number) {
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
                System.out.println("El número es menor o igual a 5");
                break;
            case 6:
                System.out.println("El número es igual a 6");
                break;
            default:
                System.out.println("El número es mayor que 6");
                break;
        }

La sentencia switch ha ido añadiendo mejoras en su código a lo largo de las versiones de Java. Por ejemplo, desde la versión 13 podemos devolver valores a través del operador flecha sin necesidad de utilizar break:

        switch (numero) {
            case 1 -> System.out.println("El número es 1");
            case 2 -> System.out.println("El número es 2");
            case 3 -> System.out.println("El número es 3");
            case 4 -> System.out.println("El número es 4");
            default -> System.out.println("El número es mayor que 4");
        }

Operador ternario

El operador ternario se puede utilizar para simplificar las sentencias if-else básicas. Su sintaxis es la siguiente:

variable = (condición)?valor_si_verdader:valor_si_falso;

Por ejemplo, podemos sustituir el siguiente código:

        if (a<5) {
            frase = "El número es menor que 5";
        } else {
            frase = "El número es mayor que 5";
        }

por este otro usando el operador ternario:

frase = (a<5)? "El número es menor que 5": "El número es mayor que 5";

Iteración

La iteración (o bucles) son bloques de código que se repiten mientras se cumpla una condición. Aunque cada lenguaje de programación puede tener sus propios bucles, existen 3 tipos que están presentes en casi todos ellos: while, do-while y for.

while

Este bucle evalúa primero una condición, y repite el código de su bloque mientras esa condición sea cierta:

class SentenciaWhileDo {

    public static void main(String[] args) {
        int number = 1;

        while (number < 10) {
            System.out.println(number);
            number++;
        }
    }

}

1
2
3
4
5
6
7
8
9

En este ejemplo, el bucle se ejecutará hasta que la variable number sea mayor o igual que 10. En cada iteración, comprueba si number es menor que 10, y si es así, ejecuta el bloque de código y vuelve a ejecutar la comprobación. Este ciclo terminar cuando la condición del bucle es false.

La condición de salida del bucle es un punto crítico. Mira el ejemplo anteior y piensa que pasaría si no existiera la siguiente línea dentro del bucle:
number++;

Si no aumentáramos el valor de la variable number en cada iteración, la condición del bucle (number < 10) siempre sería true, con lo que el bucle no terminaría nunca. Esto se conoce como bucle infinito, y es primordial detectarlos, ya que puede hacer que nuestra aplicación consuma todos los recursos de la máquina donde se está ejecutando.

do-while

La sentencia do-while es prácticamente igual que la sentencia while con una diferencia importante: mientras que el código de la sentencia do-while se ejecuta siempre al menos una vez, el de la sentencia while no tiene porqué. Esto es debido a que la condición en la sentencia while se evalúa antes de la ejecución del código, mientras que el de la sentencia do-while se evalúa despues. Por lo demás, la sintaxis y el funcionamiento de ambos bucles son casi iguales.

class SentenciaDoWhile {

    public static void main(String[] args) {
        int number = 1;

        do {
            System.out.println(number);
            number++;
        } while (number < 10);
    }

}

Para entender la diferencia entre ambos, ejecuta el siguiente código:

        number = 10;
        do {
            System.out.println("Bucle do-while: " + number);
            number++;
        } while(number < 10);

        number = 10;

        while (number < 10) {
            System.out.println("Bucle while: " + number);
            number++;            
        }

Bucle do-while: 10

Como ves, aunque la condición de salida de ambos bucles es la misma (number >= 10), el bucle do-while se ejecuta una vez, ya que la comprobación está al final del bucle.

for

En los bucles anteriores (while y do-while) no podemos saber, a priori, cuantas veces se van a ejecutar. Esa es la principal diferencia entre éstos y el bucle for. Su sintaxis báisca es la siguiente:

class SentenciaFor {

    public static void main(String[] args) {
        for (int number = 1; number < 10; number++) {
            System.out.println(number);
        }
    }

}

1
2
3
4
5
6
7
8
9

En este bucle, primero definimos una variable y le damos un valor incial (int number = 1), después escribimos la condición del bucle (number < 10) y, por último, le decimos al bucle cuanto tiene que incrementar (o decrementar) el valor de la variable en cada iteración (number++).

Este tipo de bucles era muy utilizado para recorrer arrays (un conjunto de elementos del mismo tipo), aunque, con el auge de la programación funcional se utilizan cada vez menos (lo que no quiere que no sean muy útiles en según qué circunstancias).

La sentencias break y continue

Podemos forzar al bucle a terminar su ejecución con la sentencia break:

    public static void main(String[] args) {
        for(int i=0;i<10;i++) {
            System.out.println(i);
            if(i>4) {
                break;
            }
        }
    }

0
1
2
3
4
5

En el ejemplo anterior, salimos del bucle cuando la variable i es mayor que 4.

Por otro lado, podemos indicarle al bucle que termine la iteración actual con la sentencia continue, con lo que no seguirá ejecutando el código restante y saltará a la siguiente iteración:

        for(int i=0;i<10;i++) {
            if(i==4) {
                continue;
            }
            System.out.println(i);
        }

0
1
2
3
5
6
7
8
9

En este ejemplo, si el valor de i es 4, usamos la sentencia continue para evitar que ejecute la sentencia System.out.println() y salte a la siguiente iteración (i = 5).

Bucles anidados

En los ejemplos anteriores hemos visto como usar bucles simples. Podemos crear estructuras más complejas anidando bucles (ejecutando bucles dentro de otros). Por ejemplo, podemos sacar todas las tablas de multiplicar del 1 al 9 anidando dos bucles for:

        for(int i=1; i<10; i++) {
            for(int j=1; j<10;j++) {
                System.out.println(i + "*" + j + "=" + (i*j));
            }
        }

Podemos anidar todos los bucles que queramos, aunque no debemos abusar de esta técnica, ya que añade complejidad al código (y a su legibilidad).

¿Qué bucle elegir?

Aunque siempre se pueden resolver cualquier problema con cualquier tipo de bucle (aunque algunos con más complejidad), existen situaciones donde es más conveniente utilizar uno de los 3 tipos de bucles:

Ejercicios

Ejercicio 1.a

Crea luna aplicación que pregunte la edad y muestre la frase “Puedes pasar” o “No puedes pasar” si es mayor de edad o no. Utiliza la sentencia if-else.

Ejericicio 1.b

Modifica el ejercicio anterior para usar solo una sentencia if, sin sentencia else.

Ejercicio 1.c

Modifica el primer ejercicio para utilizar el operador ternario.

Ejercicio 2.a

Escribe un programa que calcule el mayor de dos números introducidos por teclado.

Ejercicio 2.b

Usa el operador ternario para calcular el mayor de los dos números del ejercicio anterior.

Ejercicio 3.a

Haz un programa que calcule si un número introducido por teclado es par o impar usando sentencias if-else.

Ejercicio 3.b

Modifica el ejercicio anterior para calcular si el número es par o impar usando el operado ternario. Para ello, crea una variable par de tipo boolean.

Ejercicio 4.a

Escribe un programa que pida un color por teclado. Si el color es rojo la aplicación escribirá “Color de la sangre”. Si es azul, “Color del cielo”. Si es amarillo, “Color del sol”. Si no es ninguno de ese color, se mostrará el texto “Lo siento, ese color es muy aburrido”.

Ejercicio 4.b

Modifica el programa anterior para que la aplicación no distinga entre mayúsculas o minúsculas (si el usuario introduce Rojo funcionará igual que si introduce rojo).

Ejercicio 4.c

Modifica el programa anterior para que a la hora de evaluar el color, no tenga en cuenta los espacios en blanco del principio y el final. Es decir, si el usuario introduce

"    Rojo"

la aplicación funcionará igual.

Ejercicio 5.a

Escribe un programa que muestre el factorial de un número introducido por pantalla usando un bucle while.

Ejercicio 5.b

Modifica el programa anterior utilizando un bucle for

Ejercicio 6.a

Escribe un programa que pida un número por pantalla. La salida será todos los números pares que hay entre 1 y ese número. Usa un bucle while

Ejercicio 6.b

Modifica el programa anterior usando un bucle for

Ejercicio 6.c

Modifica el programa anterior (puedes usar el bucle while o el for) utilizando la sentencia continue dentro del bucle.

Ejercicio 6.d

Haz que la salida del programa anterior se muestre como una cadena de números separados por coma. Ejemplo:

Escribe un número:17
2, 4, 6, 8, 10, 12, 14, 16

Ejercicio 7.a

Escribe un programa que, dado un número encuentre el primer múltiplo de éste (excluyendo el mismo número). No utilices la sentencia break.

Ejercicio 7.b

Modifica el programa anterior utilizando la sentencia break dentro del bucle.

Ejercicio 8.a

Escribe un programa que muestre la tabla de multiplicar de un número introducido por el usuario.

Ejercicio 8.b

Haz que el programa anterior muestre, además de la tabla de multiplicar, el sumatorio desde 1 hasta el resultado (es decir, si el resultado es 6, calculará 6+5+4+3+2+1). Ejemplo de salida:

Escribe un número:3
3*1 = 3
Suma: 6
3*2 = 6
Suma: 21
3*3 = 9
Suma: 45
3*4 = 12
Suma: 78
3*5 = 15
Suma: 120
3*6 = 18
Suma: 171
3*7 = 21
Suma: 231
3*8 = 24
Suma: 300
3*9 = 27
Suma: 378

Ejercicio 9a

Haz un programa que muestre un menú con 3 opciones. Cuando el usuario elija una opción, la aplicación mostrará por pantalla “Ha elegido la opción número_opcion”. Ejemplo:

1.- Opción A
2.- Opción B
3.- Opción C
--------------------
Opción:2
Ha elegido la opción2

Ejercicio 9.b

Haz que el menú anterior tenga una última opción que sea “0.- salir”. La aplicación no terminará hasta que el usuario escoja la opción 0.