Módulo 05: Estructuras condicionales

Objetivos del módulo

  • Construir expresiones booleanas correctas con operadores relacionales y lógicos
  • Tomar decisiones con if, if-else y la escalera if-else-if
  • Anidar condicionales y saber cuándo conviene simplificarlos
  • Entender qué es un bloque y el ámbito de las variables declaradas dentro de él
  • Usar el operador ternario ? : en los casos en que mejora la legibilidad
  • Dominar el switch clásico (break, fallthrough, default) y las expresiones switch de Java 14+ (->, yield, exhaustividad)
  • Usar switch con int, char y String
  • Comparar cadenas con equals y entender por qué == no sirve
  • Reconocer y evitar los errores típicos de los condicionales
  • Traducir cada estructura entre diagrama de flujo, pseudocódigo y Java

1. Programas que toman decisiones

Hasta ahora todos tus programas eran secuenciales: las instrucciones se ejecutaban una detrás de otra, de arriba abajo, siempre todas y siempre en el mismo orden. Pero casi ningún problema real es así. Piensa en cosas que haces cada día:

  • Si llueve, cojo el paraguas.
  • Si el semáforo está en verde, cruzo; si no, espero.
  • Según el día de la semana, voy a clase, a trabajar o descanso.

📘 Concepto: Una estructura condicional (o de selección) permite que el programa elija qué instrucciones ejecutar en función de una condición, es decir, de una expresión que solo puede ser verdadera (true) o falsa (false).

En el módulo 02 viste las tres estructuras de control de la programación estructurada: secuencia, selección e iteración. Con ellas se puede escribir cualquier algoritmo (teorema de Böhm-Jacopini). En este módulo traducimos la selección a Java y en el siguiente, la iteración.

Estructura Pseudocódigo Java
Selección simple Si ... Entonces ... FinSi if (...) { ... }
Selección doble Si ... Entonces ... SiNo ... FinSi if (...) { ... } else { ... }
Selección múltiple Si ... SiNo Si ... / Segun ... Hacer if-else-if / switch

Para cada estructura verás tres representaciones del mismo algoritmo: el diagrama de flujo, el pseudocódigo y el código Java. Así podrás pasar de una a otra con soltura, que es justo lo que se pide en los exámenes de primer curso.


2. Expresiones booleanas

Toda decisión depende de una condición, y en Java una condición es siempre una expresión de tipo boolean. No vale un número (como en C o JavaScript): if (1) es un error de compilación en Java.

2.1. Operadores relacionales

Comparan dos valores y devuelven true o false:

Operador Significado Ejemplo Resultado si x = 7
== igual a x == 7 true
!= distinto de x != 7 false
< menor que x < 5 false
<= menor o igual que x <= 7 true
> mayor que x > 0 true
>= mayor o igual que x >= 10 false

⚠️ Importante: = es asignación («guarda este valor») y == es comparación («¿son iguales?»). Confundirlos es el error número uno de los principiantes. Lo verás en la sección de errores típicos.

2.2. Operadores lógicos

Combinan condiciones:

Operador Nombre Es true cuando… Ejemplo
&& Y lógico (AND) las dos partes son true edad >= 18 && tieneCarne
\|\| O lógico (OR) al menos una parte es true edad < 26 \|\| edad >= 65
! NO lógico (NOT) la expresión es false !tieneCarne

Tabla de verdad completa:

a b a && b a \|\| b !a
true true true true false
true false false true false
false true false true true
false false false false true

2.3. Evaluación en cortocircuito

Java evalúa && y || de izquierda a derecha y se detiene en cuanto conoce el resultado:

  • En a && b, si a es false, el resultado ya es false y b no se evalúa.
  • En a || b, si a es true, el resultado ya es true y b no se evalúa.

Esto no es un detalle académico: sirve para proteger la segunda condición. En divisor != 0 && dividendo / divisor > 2, si el divisor es cero nunca se llega a dividir, así que no se produce la excepción ArithmeticException.

💡 Consejo: Existen también & y | aplicados a boolean, que evalúan siempre las dos partes. No los uses en condiciones: casi nunca es lo que quieres. Utilízalos solo en operaciones con bits, que no son objeto de este curso.

2.4. Precedencia

De mayor a menor prioridad: ! → aritméticos (*, /, %, +, -) → relacionales (<, <=, >, >=) → igualdad (==, !=) → && → || → ternario ? : → asignación =.

Por eso a || b && c significa a || (b && c). Aunque conozcas la tabla, usa paréntesis cuando mezcles && y ||: el lector lo agradecerá.

2.5. Leyes de De Morgan

Sirven para negar condiciones compuestas sin equivocarte:

  • !(a && b) equivale a !a || !b
  • !(a || b) equivale a !a && !b

Ejemplo: «no es cierto que la edad esté entre 18 y 65» → !(edad >= 18 && edad <= 65) → edad < 18 || edad > 65.

💻 Ejemplo: Todos estos operadores en acción:

public class Booleanas {
    public static void main(String[] args) {
        int edad = 20;
        boolean tieneCarne = false;

        // Relacionales: el resultado siempre es true o false
        System.out.println("edad >= 18    -> " + (edad >= 18));
        System.out.println("edad != 20    -> " + (edad != 20));

        // Lógicos
        System.out.println("conducir      -> " + (edad >= 18 && tieneCarne));
        System.out.println("descuento     -> " + (edad < 26 || edad >= 65));
        System.out.println("!tieneCarne   -> " + !tieneCarne);
        System.out.println("entre 18 y 65 -> " + (edad >= 18 && edad <= 65));

        // Cortocircuito: como divisor es 0, la división NO llega a hacerse
        int dividendo = 10, divisor = 0;
        System.out.println("cortocircuito -> " + (divisor != 0 && dividendo / divisor > 2));

        // De Morgan: !(a && b) es lo mismo que !a || !b
        boolean a = true, b = false;
        System.out.println("!(a && b)     -> " + !(a && b));
        System.out.println("!a || !b      -> " + (!a || !b));
    }
}

Salida:

edad >= 18    -> true
edad != 20    -> false
conducir      -> false
descuento     -> true
!tieneCarne   -> true
entre 18 y 65 -> true
cortocircuito -> false
!(a && b)     -> true
!a || !b      -> true

⚠️ Importante: En matemáticas escribes 18 <= edad <= 65, pero en Java eso no compila (18 <= edad da un boolean, y no se puede comparar un boolean con 65). Siempre se escribe con dos comparaciones unidas por &&: edad >= 18 && edad <= 65.

💡 Consejo: Guarda las condiciones complicadas en variables boolean con nombre: boolean puedeConducir = edad >= 18 && tieneCarne;. Después, if (puedeConducir) se lee como una frase. Y nunca escribas if (puedeConducir == true): es redundante.


3. Condicional simple: if

El if ejecuta un bloque de instrucciones solo si la condición es verdadera. Si es falsa, el bloque se salta y el programa continúa después de él.

Diagrama de flujo de un condicional simple: si edad >= 18 se escriben dos mensajes; si no, se salta el bloque

Figura 1. Diagrama de flujo del condicional simple.

Pseudocódigo:

Algoritmo MayorDeEdad
    Escribir "Introduce tu edad: "
    Leer edad
    Si edad >= 18 Entonces
        Escribir "Eres mayor de edad."
        Escribir "Ya puedes votar."
    FinSi
    Escribir "Fin del programa."
FinAlgoritmo

Sintaxis en Java: if (condición) { instrucciones }.

  • La condición va siempre entre paréntesis.
  • Las instrucciones van entre llaves { }, que forman un bloque.
  • No se pone ; después de if (condición).
import java.util.Scanner;

public class MayorDeEdad {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce tu edad: ");
        int edad = sc.nextInt();

        if (edad >= 18) {
            System.out.println("Eres mayor de edad.");
            System.out.println("Ya puedes votar.");
        }

        System.out.println("Fin del programa.");
    }
}

Dos ejecuciones:

Introduce tu edad: 20
Eres mayor de edad.
Ya puedes votar.
Fin del programa.

Introduce tu edad: 15
Fin del programa.

Con 15 años la condición es falsa: las dos líneas del bloque no se ejecutan, pero «Fin del programa.» sí, porque está fuera del if.

💡 Consejo: Si el bloque tiene una sola instrucción, Java permite omitir las llaves (if (edad >= 18) System.out.println("...");). No lo hagas: es fuente de errores (sección 13) y casi todas las guías de estilo profesionales, como la de Google, exigen llaves siempre.


4. Condicional doble: if-else

Cuando hay que hacer una cosa u otra, se añade else («si no»). Se ejecuta exactamente uno de los dos bloques, nunca los dos y nunca ninguno.

Diagrama de flujo de un condicional doble que escribe par o impar

Figura 2. Diagrama de flujo del condicional doble.

Algoritmo ParImpar
    Escribir "Introduce un número entero: "
    Leer n
    Si n MOD 2 = 0 Entonces
        Escribir n, " es par."
    SiNo
        Escribir n, " es impar."
    FinSi
FinAlgoritmo
import java.util.Scanner;

public class ParImpar {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce un número entero: ");
        int n = sc.nextInt();

        if (n % 2 == 0) {
            System.out.println(n + " es par.");
        } else {
            System.out.println(n + " es impar.");
        }
    }
}
Introduce un número entero: 7
7 es impar.

📘 Concepto: n % 2 es el resto de dividir n entre 2 (operador módulo del módulo 03). Si es 0, el número es par. Ojo: con negativos, -7 % 2 vale -1, no 1; por eso se compara con == 0 y no con == 1 para detectar impares.


5. Condicional múltiple: la escalera if-else-if

Cuando hay más de dos caminos posibles, se encadenan varios else if. Java evalúa las condiciones en orden, de arriba abajo, ejecuta el bloque de la primera que sea verdadera y se salta todas las demás. El else final (opcional) recoge el caso en que ninguna se cumple.

Diagrama de flujo en escalera para convertir una nota numérica en texto

Figura 3. Diagrama de flujo de la escalera if-else-if.

Algoritmo Calificacion
    Escribir "Introduce la nota (0-10): "
    Leer nota
    Si nota < 0 O nota > 10 Entonces
        texto <- "Nota no válida"
    SiNo Si nota < 5 Entonces
        texto <- "Insuficiente"
    SiNo Si nota < 6 Entonces
        texto <- "Suficiente"
    SiNo Si nota < 7 Entonces
        texto <- "Bien"
    SiNo Si nota < 9 Entonces
        texto <- "Notable"
    SiNo
        texto <- "Sobresaliente"
    FinSi
    Escribir "Calificación: ", texto
FinAlgoritmo
import java.util.Scanner;

public class Calificacion {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce la nota (0-10): ");
        double nota = sc.nextDouble();

        String texto;
        if (nota < 0 || nota > 10) {
            texto = "Nota no válida";
        } else if (nota < 5) {
            texto = "Insuficiente";
        } else if (nota < 6) {
            texto = "Suficiente";
        } else if (nota < 7) {
            texto = "Bien";
        } else if (nota < 9) {
            texto = "Notable";
        } else {
            texto = "Sobresaliente";
        }

        System.out.println("Calificación: " + texto);
    }
}

Dos ejecuciones, una con una nota válida y otra fuera de rango:

Introduce la nota (0-10): 7.5
Calificación: Notable

Introduce la nota (0-10): 11
Calificación: Nota no válida

Fíjate en que la segunda condición es solo nota < 5, no nota >= 0 && nota < 5: si llegamos a ella es porque la primera fue falsa, así que ya sabemos que la nota está entre 0 y 10. Cada escalón aprovecha lo que descartaron los anteriores.

⚠️ Importante: Los resultados de este curso se generaron con la configuración regional inglesa, por eso escribimos 7.5 y printf muestra 127.50. Si tu sistema está en español, Scanner espera coma decimal (7,5) y lanzará InputMismatchException si escribes un punto; printf mostrará 127,50. Lo viste en el módulo 04: si quieres usar siempre el punto, crea el lector con new Scanner(System.in).useLocale(java.util.Locale.US).

5.1. El orden de las condiciones importa

Como solo se ejecuta la primera rama verdadera, un orden incorrecto produce resultados absurdos:

double nota = 9.5;
if (nota >= 5) {                         // ¡MAL! Es cierta y "se come" a las demás
    System.out.println("Aprobado");      // se imprime "Aprobado"
} else if (nota >= 7) {
    System.out.println("Notable");       // nunca se ejecuta
} else if (nota >= 9) {
    System.out.println("Sobresaliente"); // nunca se ejecuta
}

Con un 9.5 debería salir «Sobresaliente», pero nota >= 5 ya es cierta y las demás ramas nunca se evalúan. Hay dos soluciones: ordenar de más restrictiva a menos (>= 9, luego >= 7, luego >= 5) o hacerlo como en Calificacion, comparando con < de menor a mayor.

5.2. Escalera frente a varios if independientes

No es lo mismo. Con x = 20:

// Escalera: como máximo se ejecuta UNA rama -> solo imprime "positivo"
if (x > 0) {
    System.out.println("positivo");
} else if (x > 10) {
    System.out.println("mayor que 10");
}

// if independientes: se evalúan TODOS -> imprime las dos líneas
if (x > 0) { System.out.println("positivo"); }
if (x > 10) { System.out.println("mayor que 10"); }

Usa la escalera cuando los casos sean excluyentes (una nota solo tiene una calificación) y varios if cuando sean independientes (un usuario puede ser a la vez mayor de edad y socio).


6. Condicionales anidados

Un condicional anidado es un if dentro de otro if (o dentro de un else). Se usa cuando una decisión solo tiene sentido después de haber tomado otra.

Ejemplo: una tienda hace un 15 % de descuento a los socios que gastan 100 € o más, un 5 % a los socios que gastan menos y también un 5 % a los no socios que gastan 100 € o más.

Diagrama de flujo con dos condicionales anidados para calcular el descuento

Figura 4. Condicionales anidados: cada rama del primer if contiene otro if.

Algoritmo Descuento
    Leer importe, socio
    Si socio = 1 Entonces
        Si importe >= 100 Entonces
            descuento <- 0.15
        SiNo
            descuento <- 0.05
        FinSi
    SiNo
        Si importe >= 100 Entonces
            descuento <- 0.05
        SiNo
            descuento <- 0
        FinSi
    FinSi
    total <- importe - importe * descuento
    Escribir descuento, total
FinAlgoritmo
import java.util.Scanner;

public class Descuento {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Importe de la compra (€): ");
        double importe = sc.nextDouble();
        System.out.print("¿Eres socio? (1 = sí, 0 = no): ");
        int socio = sc.nextInt();

        double descuento;
        if (socio == 1) {
            // Condicional anidado: solo se evalúa si es socio
            if (importe >= 100) {
                descuento = 0.15;
            } else {
                descuento = 0.05;
            }
        } else {
            if (importe >= 100) {
                descuento = 0.05;
            } else {
                descuento = 0;
            }
        }

        double total = importe - importe * descuento;
        System.out.printf("Descuento aplicado: %.0f %%%n", descuento * 100);
        System.out.printf("Total a pagar: %.2f €%n", total);
    }
}
Importe de la compra (€): 150
¿Eres socio? (1 = sí, 0 = no): 1
Descuento aplicado: 15 %
Total a pagar: 127.50 €

💡 Consejo: En printf, %% escribe un símbolo % literal. Por eso el formato es "%.0f %%%n".

6.1. Cuándo aplanar un anidamiento

Dos if anidados sin else equivalen a uno solo con &&: if (edad >= 18) { if (tieneCarne) { ... } } es lo mismo que if (edad >= 18 && tieneCarne) { ... }, que se lee mejor.

Más de tres niveles de anidamiento hacen el código difícil de leer (el famoso «código en flecha»). Cuando te pase, plantéate combinar condiciones, usar una escalera o, a partir del módulo 07, sacar parte del código a un método.

6.2. ¿A qué if pertenece el else?

Un else siempre pertenece al if más cercano que no tenga ya su else, independientemente de cómo esté sangrado el código:

if (a > 0)
    if (b > 0)
        System.out.println("los dos positivos");
else                                   // ¡pertenece a if (b > 0)!
    System.out.println("a no es positivo");

Si a = 5 y b = -1, se imprime «a no es positivo», que es falso. Es el problema del else colgante (dangling else) y desaparece en cuanto pones llaves: con if (a > 0) { if (b > 0) { ... } } else { ... } queda claro que el else pertenece al if externo.


7. Bloques y ámbito de las variables

📘 Concepto: Un bloque es un grupo de instrucciones entre llaves { }. El ámbito (scope) de una variable es la zona del código donde existe y se puede usar: desde su declaración hasta la llave que cierra el bloque en el que se declaró.

Consecuencias prácticas:

  1. Una variable declarada dentro de un if desaparece al salir de él.
  2. Una variable declarada fuera (antes) del if se puede leer y modificar dentro, y conserva el valor al salir.
  3. No puedes declarar dentro de un bloque una variable con el mismo nombre que otra local que siga existiendo fuera (Java lo prohíbe para evitar confusiones).
public class Ambito {
    public static void main(String[] args) {
        int puntos = 120;               // visible en todo main
        String nivel = "básico";        // declarada FUERA del if

        if (puntos >= 100) {
            int extra = puntos - 100;   // solo existe dentro de este bloque
            nivel = "avanzado";         // modifica la variable de fuera
            System.out.println("Puntos extra: " + extra);
        }

        System.out.println("Nivel: " + nivel);
        // System.out.println(extra);   // ERROR: extra ya no existe aquí
    }
}
Puntos extra: 20
Nivel: avanzado

Si quitas el comentario de la última línea, el compilador protesta:

public class AmbitoError {
    public static void main(String[] args) {
        int puntos = 120;
        if (puntos >= 100) {
            int extra = puntos - 100;
        }
        System.out.println(extra);
    }
}
AmbitoError.java:7: error: cannot find symbol
        System.out.println(extra);
                           ^
  symbol:   variable extra
  location: class AmbitoError
1 error

💡 Consejo: Cuando necesites un valor calculado dentro de un if después del if, declara la variable antes y asígnale el valor en cada rama, como hacemos con texto en Calificacion. El compilador comprueba que la variable tenga valor en todos los caminos posibles; si te olvidas de una rama, verás el error variable texto might not have been initialized.


8. El operador ternario ? :

Es una forma compacta de un if-else que devuelve un valor:

variable = condición ? valorSiTrue : valorSiFalse;

Se llama ternario porque tiene tres operandos. Es una expresión (produce un valor), no una sentencia: puedes usarlo dentro de una asignación, de un println o de una concatenación.

public class Ternario {
    public static void main(String[] args) {
        int a = 8, b = 13;

        // Equivale a: if (a > b) { mayor = a; } else { mayor = b; }
        int mayor = (a > b) ? a : b;
        System.out.println("Mayor: " + mayor);

        // Muy útil para elegir un texto
        int manzanas = 1;
        System.out.println("Tengo " + manzanas + (manzanas == 1 ? " manzana" : " manzanas"));
        manzanas = 3;
        System.out.println("Tengo " + manzanas + (manzanas == 1 ? " manzana" : " manzanas"));

        double nota = 4.9;
        String resultado = nota >= 5 ? "APTO" : "NO APTO";
        System.out.println("Resultado: " + resultado);
    }
}
Mayor: 13
Tengo 1 manzana
Tengo 3 manzanas
Resultado: NO APTO

⚠️ Importante: Usa el ternario solo para elecciones simples entre dos valores. Si anidas ternarios (a ? b : c ? d : e) o pones acciones complicadas dentro, el código se vuelve ilegible: usa un if-else normal. Los dos valores deben ser de tipos compatibles.


9. El switch clásico

Cuando la decisión consiste en comparar una misma variable con una lista de valores concretos, una escalera if-else-if funciona, pero queda larga. Para eso existe switch. En pseudocódigo es la estructura Segun:

Algoritmo DiaSemana
    Escribir "Número de día (1-7): "
    Leer dia
    Segun dia Hacer
        1: Escribir "Lunes"
        2: Escribir "Martes"
        3: Escribir "Miércoles"
        4: Escribir "Jueves"
        5: Escribir "Viernes"
        6: Escribir "Sábado"
        7: Escribir "Domingo"
        De Otro Modo: Escribir "Día no válido"
    FinSegun
FinAlgoritmo

En Java, la forma clásica (la que existe desde Java 1.0 y encontrarás en mucho código y en libros) es:

import java.util.Scanner;

public class DiaSemana {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Número de día (1-7): ");
        int dia = sc.nextInt();

        switch (dia) {
            case 1:
                System.out.println("Lunes");
                break;
            case 2:
                System.out.println("Martes");
                break;
            case 3:
                System.out.println("Miércoles");
                break;
            case 4:
                System.out.println("Jueves");
                break;
            case 5:
                System.out.println("Viernes");
                break;
            case 6:
                System.out.println("Sábado");
                break;
            case 7:
                System.out.println("Domingo");
                break;
            default:
                System.out.println("Día no válido");
        }
    }
}
Número de día (1-7): 3
Miércoles

Cómo funciona:

  1. Se evalúa la expresión entre paréntesis (dia).
  2. Se salta a la etiqueta case cuyo valor coincide.
  3. Se ejecutan las instrucciones desde ahí hasta encontrar un break (o el final del switch).
  4. Si ningún case coincide, se salta a default (si existe).

Comparación entre un switch con break, donde solo se ejecuta un case, y uno sin break, donde la ejecución cae a los siguientes

Figura 5. switch clásico con break y sin break.

9.1. El fallthrough (caída en cascada)

Si olvidas un break, la ejecución continúa en el siguiente case aunque su valor no coincida. A esto se le llama fallthrough:

public class SinBreak {
    public static void main(String[] args) {
        int dia = 5;

        // Se nos han olvidado los break...
        switch (dia) {
            case 5:
                System.out.println("Viernes");
            case 6:
                System.out.println("Sábado");
            case 7:
                System.out.println("Domingo");
            default:
                System.out.println("Día no válido");
        }
    }
}
Viernes
Sábado
Domingo
Día no válido

Casi siempre es un error, pero a veces se usa a propósito para que varios valores compartan el mismo código:

public class DiasMesClasico {
    public static void main(String[] args) {
        int mes = 9;
        int dias;

        // Fallthrough intencionado: varios case comparten el mismo código
        switch (mes) {
            case 1: case 3: case 5: case 7: case 8: case 10: case 12:
                dias = 31;
                break;
            case 4: case 6: case 9: case 11:
                dias = 30;
                break;
            case 2:
                dias = 28;   // sin tener en cuenta los bisiestos
                break;
            default:
                dias = -1;   // mes no válido
        }

        System.out.println("El mes " + mes + " tiene " + dias + " días.");
    }
}

Salida: El mes 9 tiene 30 días.

9.2. Reglas del switch

  • La expresión puede ser de tipo int, short, byte, char (y sus envoltorios), String o un enum (módulo 14). No puede ser long, float, double ni boolean en el switch clásico.
  • Los valores de los case deben ser constantes (literales o final) y no pueden repetirse.
  • No se pueden poner rangos: case nota >= 5: no compila. Para rangos, usa if-else-if.
  • default es opcional, puede ir en cualquier posición (por convención, al final) y no necesita break si es el último.

💡 Consejo: Pon siempre un default, aunque «sea imposible» llegar a él: te avisará de datos inesperados en lugar de que el programa falle en silencio.


10. Expresiones switch (Java 14+)

El switch clásico tiene dos problemas: el break olvidado y la verbosidad. Desde Java 14 existe una sintaxis nueva con flechas -> que los resuelve. Es la que te recomendamos para código nuevo.

10.1. switch con flechas como sentencia

int nivel = 2;
switch (nivel) {
    case 1 -> System.out.println("Nivel fácil");
    case 2 -> {                                   // bloque: varias instrucciones
        System.out.println("Nivel medio");
        System.out.println("Tienes 3 vidas");
    }
    case 3 -> System.out.println("Nivel difícil");
    default -> System.out.println("Nivel desconocido");
}
// Imprime "Nivel medio" y "Tienes 3 vidas". No hace falta break.

Diferencias con el clásico:

  • Con -> nunca hay fallthrough: se ejecuta solo lo que hay a la derecha de la flecha. Adiós a break.
  • A la derecha va una instrucción o un bloque entre llaves.
  • Varios valores se separan con comas en un mismo case: case 6, 7 -> ....

10.2. switch como expresión: devuelve un valor

La gran novedad es que el switch puede producir un valor, igual que el ternario, y asignarse a una variable. Observa el ; final: es una asignación.

import java.util.Scanner;

public class DiasMes {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Número de mes (1-12): ");
        int mes = sc.nextInt();
        System.out.print("Año: ");
        int anio = sc.nextInt();

        int dias = switch (mes) {
            case 1, 3, 5, 7, 8, 10, 12 -> 31;
            case 4, 6, 9, 11 -> 30;
            case 2 -> {
                boolean bisiesto = (anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0;
                yield bisiesto ? 29 : 28;   // yield devuelve el valor del bloque
            }
            default -> -1;
        };

        if (dias == -1) {
            System.out.println("Mes no válido");
        } else {
            System.out.println("El mes " + mes + " de " + anio + " tiene " + dias + " días.");
        }
    }
}
Número de mes (1-12): 2
Año: 2024
El mes 2 de 2024 tiene 29 días.

Número de mes (1-12): 2
Año: 2025
El mes 2 de 2025 tiene 28 días.
  • case 1, 3, 5, 7, 8, 10, 12 -> 31; agrupa valores sin fallthrough.
  • Si una rama necesita varias instrucciones, se usa un bloque y yield para indicar el valor que devuelve. yield es al switch lo que return es a un método (lo verás en el módulo 07).

10.3. Exhaustividad

Una expresión switch debe cubrir todos los valores posibles, porque siempre tiene que devolver algo. Con un int es imposible enumerar todos sus valores, así que el default es obligatorio:

public class SwitchNoExhaustivo {
    public static void main(String[] args) {
        int dia = 3;
        String nombre = switch (dia) {
            case 1 -> "Lunes";
            case 2 -> "Martes";
            case 3 -> "Miércoles";
        };
        System.out.println(nombre);
    }
}
SwitchNoExhaustivo.java:4: error: the switch expression does not cover all possible input values
        String nombre = switch (dia) {
                        ^
1 error

Con un enum (módulo 14) sí puedes cubrir todas las constantes sin default, y el compilador te avisará si algún día añades una nueva y te olvidas de tratarla. Esa comprobación es una de las grandes ventajas del switch moderno.

10.4. Comparativa

Característica switch clásico (case X:) switch con flechas (case X ->)
Disponible desde Java 1.0 Java 14
Necesita break Sí (si no, fallthrough) No, nunca hay fallthrough
Varios valores por case Apilando etiquetas case 1: case 2: Con comas case 1, 2 -> (también vale case 1, 2: en el clásico desde Java 14)
Puede devolver un valor No (se asigna en cada rama) Sí, como expresión (con yield en bloques)
Exhaustividad comprobada No Sí, cuando se usa como expresión

📘 Concepto: Desde Java 21, switch también admite patrones (por ejemplo, case Integer i when i > 0 ->), que permiten decidir según el tipo de un objeto. Lo estudiarás en el módulo 14, cuando conozcas las clases.


11. switch con String y con char

Desde Java 7, switch admite cadenas. La comparación se hace con equals (distingue mayúsculas de minúsculas), así que conviene normalizar antes con toLowerCase():

import java.util.Scanner;

public class SwitchString {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Escribe un día de la semana: ");
        String dia = sc.next().toLowerCase();   // "Sábado" -> "sábado"

        String tipo = switch (dia) {
            case "lunes", "martes", "miércoles", "jueves", "viernes" -> "laborable";
            case "sábado", "domingo" -> "fin de semana";
            default -> "desconocido";
        };

        System.out.println("El " + dia + " es " + tipo + ".");
    }
}
Escribe un día de la semana: Sábado
El sábado es fin de semana.

Con char, los valores de los case se escriben entre comillas simples:

import java.util.Scanner;

public class SwitchChar {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Escribe una letra: ");
        char letra = sc.next().charAt(0);   // primer carácter de lo escrito

        switch (letra) {
            case 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U' ->
                System.out.println("'" + letra + "' es una vocal");
            case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ->
                System.out.println("'" + letra + "' es un dígito");
            default ->
                System.out.println("'" + letra + "' no es vocal ni dígito");
        }
    }
}
Escribe una letra: E
'E' es una vocal

💡 Consejo: sc.next().charAt(0) lee una palabra y se queda con su primer carácter. Es la forma habitual de leer un char con Scanner, que no tiene nextChar(). Verás charAt en detalle en el módulo 09.


12. Comparar cadenas: equals y no == (adelanto)

Es muy tentador escribir if (clave == "java"). Parece funcionar en algunas pruebas… y falla en otras. Míralo:

import java.util.Scanner;

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

        System.out.print("Contraseña: ");
        String escrita = sc.next();

        System.out.println("Con == : " + (escrita == clave));
        System.out.println("Con equals: " + escrita.equals(clave));

        if (escrita.equals(clave)) {
            System.out.println("Acceso concedido");
        } else {
            System.out.println("Acceso denegado");
        }
    }
}
Contraseña: java
Con == : false
Con equals: true
Acceso concedido

El usuario escribió exactamente java, pero == dice false. ¿Por qué?

📘 Concepto: String no es un tipo primitivo sino un objeto. Una variable String no guarda el texto, sino una referencia (la dirección de memoria donde está el objeto). == compara referencias («¿son el mismo objeto?») y equals compara contenidos («¿tienen los mismos caracteres?»). El texto leído con Scanner es un objeto nuevo, distinto del literal "java", aunque contenga lo mismo.

Regla práctica que debes memorizar ya:

Quiero saber si… Escribo
Dos cadenas tienen el mismo texto a.equals(b)
…sin distinguir mayúsculas a.equalsIgnoreCase(b)
Dos primitivos (int, char, double…) son iguales a == b

En el módulo 09 entenderás los detalles (el String pool, por qué == a veces «funciona» y cómo comparar en orden alfabético con compareTo).

💡 Consejo: Si una de las cadenas es un literal, ponlo delante: "java".equals(escrita). Así, si escrita fuera null, obtendrías false en lugar de un NullPointerException.


13. Errores típicos

13.1. Punto y coma después del if

En if (edad >= 18); { ... }, el ; termina el if con una sentencia vacía. El bloque { ... } que sigue ya no pertenece al if: es un bloque suelto que se ejecuta siempre. Compila sin errores, así que es muy difícil de detectar.

13.2. if sin llaves con varias sentencias

La sangría no tiene significado en Java (no es Python). Sin llaves, el if solo controla la primera sentencia.

13.3. = en lugar de ==

Con números, el compilador te salva:

public class AsignacionEnIf {
    public static void main(String[] args) {
        int x = 3;
        if (x = 5) {
            System.out.println("x vale 5");
        }
    }
}
AsignacionEnIf.java:4: error: incompatible types: int cannot be converted to boolean
        if (x = 5) {
              ^
1 error

Pero con variables boolean compila y es un error silencioso: if (esAdmin = true) asigna true a esAdmin y luego comprueba su valor, que siempre es true.

Los tres errores en un único programa que compila sin quejas:

public class ErroresSilenciosos {
    public static void main(String[] args) {
        int edad = 12;

        // Error 1: punto y coma después del if
        if (edad >= 18); {
            System.out.println("1) Puedes entrar (¡aunque tengas " + edad + " años!)");
        }

        // Error 2: if sin llaves con dos sentencias "indentadas"
        if (edad >= 18)
            System.out.println("2) Mayor de edad");
            System.out.println("2) Puedes votar");   // ¡esta línea NO está dentro del if!

        // Error 3: = en lugar de == con boolean (compila, pero asigna)
        boolean esAdmin = false;
        if (esAdmin = true) {
            System.out.println("3) Bienvenido, administrador");
        }
    }
}
1) Puedes entrar (¡aunque tengas 12 años!)
2) Puedes votar
3) Bienvenido, administrador

Ninguna de las tres líneas debería haberse escrito, porque la edad es 12 y esAdmin era false.

13.4. Resumen de errores y soluciones

Error Síntoma Solución
if (x > 0); El bloque se ejecuta siempre Quita el ;. IntelliJ lo marca con un aviso: hazle caso
Sin llaves Solo la primera línea depende del if Pon siempre llaves
if (x = 5) incompatible types: int cannot be converted to boolean Usa ==
if (b = true) Siempre entra Escribe simplemente if (b)
cad == "hola" Falla con texto leído cad.equals("hola")
18 <= x <= 65 No compila x >= 18 && x <= 65
switch sin break Se ejecutan varios case Añade break o usa ->
Comparar double con == 0.1 + 0.2 == 0.3 es false Compara con un margen: Math.abs(a - b) < 1e-9
Ramas mal ordenadas Una rama «se come» a otras Ordena de más restrictiva a menos

💡 Consejo: Para cada condicional que escribas, prueba un valor de cada rama y los valores frontera (si la condición es nota >= 5, prueba 4.99, 5 y 5.01). La mayoría de los fallos están justo en los límites.


14. Ejemplos completos

14.1. Año bisiesto

Un año es bisiesto si es divisible entre 4, salvo que sea divisible entre 100, en cuyo caso solo lo es si también es divisible entre 400. En pseudocódigo: Si (anio MOD 4 = 0 Y anio MOD 100 <> 0) O anio MOD 400 = 0 Entonces.

import java.util.Scanner;

public class Bisiesto {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce un año: ");
        int anio = sc.nextInt();

        // Regla del calendario gregoriano:
        // divisible entre 4 y no entre 100, o divisible entre 400
        boolean bisiesto = (anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0;

        if (bisiesto) {
            System.out.println(anio + " es bisiesto (febrero tiene 29 días).");
        } else {
            System.out.println(anio + " no es bisiesto (febrero tiene 28 días).");
        }
    }
}
Introduce un año: 2024
2024 es bisiesto (febrero tiene 29 días).

Introduce un año: 1900
1900 no es bisiesto (febrero tiene 28 días).

Introduce un año: 2000
2000 es bisiesto (febrero tiene 29 días).

1900 es divisible entre 4 y entre 100 pero no entre 400: no es bisiesto. 2000 sí lo es porque es divisible entre 400.

14.2. Calculadora con menú

Combina validación con if, una condición compuesta para la división entre cero y una expresión switch:

import java.util.Scanner;

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

        System.out.println("=== CALCULADORA ===");
        System.out.println("1. Sumar  2. Restar  3. Multiplicar  4. Dividir  5. Resto");
        System.out.print("Elige una opción: ");
        int opcion = sc.nextInt();

        if (opcion < 1 || opcion > 5) {
            System.out.println("Opción no válida.");
        } else {
            System.out.print("Primer número: ");
            double a = sc.nextDouble();
            System.out.print("Segundo número: ");
            double b = sc.nextDouble();

            if ((opcion == 4 || opcion == 5) && b == 0) {
                System.out.println("Error: no se puede dividir entre cero.");
            } else {
                double resultado = switch (opcion) {
                    case 1 -> a + b;
                    case 2 -> a - b;
                    case 3 -> a * b;
                    case 4 -> a / b;
                    default -> a % b;   // solo queda la opción 5
                };
                System.out.printf("Resultado: %.2f%n", resultado);
            }
        }
    }
}
=== CALCULADORA ===
1. Sumar  2. Restar  3. Multiplicar  4. Dividir  5. Resto
Elige una opción: 4
Primer número: 17
Segundo número: 4
Resultado: 4.25

Si en otra ejecución eliges la opción 4 con un segundo número igual a 0, el programa responde Error: no se puede dividir entre cero. sin calcular nada.

📘 Concepto: Con double, dividir entre cero no lanza excepción: 7.0 / 0 da Infinity y 0.0 / 0 da NaN (Not a Number). Con int, 7 / 0 lanza ArithmeticException. En ambos casos es mejor comprobarlo antes, como hace este programa.

14.3. Tarifas por edad

Un museo cobra 10 € de entrada general. Los menores de 4 años entran gratis, de 4 a 12 pagan la mitad, los mayores de 64 pagan el 60 % y los jóvenes de hasta 25 años con carné joven tienen un 20 % de descuento.

import java.util.Scanner;

public class Tarifas {
    public static void main(String[] args) {
        final double PRECIO_GENERAL = 10.0;
        Scanner sc = new Scanner(System.in);

        System.out.print("Edad del visitante: ");
        int edad = sc.nextInt();

        if (edad < 0 || edad > 120) {
            System.out.println("Edad no válida.");
        } else {
            double precio;
            String tarifa;
            if (edad < 4) {
                tarifa = "Bebé";
                precio = 0;
            } else if (edad <= 12) {
                tarifa = "Infantil";
                precio = PRECIO_GENERAL * 0.5;
            } else if (edad < 65) {
                tarifa = "General";
                precio = PRECIO_GENERAL;
                System.out.print("¿Tienes carné joven? (1 = sí, 0 = no): ");
                int carne = sc.nextInt();
                if (carne == 1 && edad <= 25) {
                    tarifa = "Joven";
                    precio = PRECIO_GENERAL * 0.8;
                }
            } else {
                tarifa = "Senior";
                precio = PRECIO_GENERAL * 0.6;
            }
            System.out.printf("Tarifa %s: %.2f €%n", tarifa, precio);
        }
    }
}
Edad del visitante: 8
Tarifa Infantil: 5.00 €

Edad del visitante: 22
¿Tienes carné joven? (1 = sí, 0 = no): 1
Tarifa Joven: 8.00 €

Observa que solo se pregunta por el carné joven cuando tiene sentido (edad entre 13 y 64): es un condicional anidado dentro de una rama de la escalera. Y la constante PRECIO_GENERAL evita repetir el número mágico 10: si el museo sube el precio, solo cambias una línea.

14.4. Ecuación de segundo grado

Para resolver ax² + bx + c = 0 se calcula el discriminante Δ = b² − 4ac:

Caso Soluciones
a = 0 No es de segundo grado: bx + c = 0 → x = −c / b (si b ≠ 0)
Δ > 0 Dos soluciones reales: x = (−b ± √Δ) / 2a
Δ = 0 Una solución doble: x = −b / 2a
Δ < 0 Dos soluciones complejas conjugadas
flowchart TD
    A([Inicio]) --> B[/Leer a, b, c/]
    B --> C{a == 0}
    C -- Sí --> D{b == 0}
    D -- Sí --> E{c == 0}
    E -- Sí --> E1[/Infinitas soluciones/]
    E -- No --> E2[/Sin solución/]
    D -- No --> F[/x = -c ÷ b/]
    C -- No --> G[d = b² - 4ac]
    G --> H{d > 0}
    H -- Sí --> H1[/Dos soluciones reales/]
    H -- No --> I{d == 0}
    I -- Sí --> I1[/Solución doble/]
    I -- No --> I2[/Soluciones complejas/]
import java.util.Scanner;

public class Ecuacion {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Coeficientes a, b y c de ax² + bx + c = 0: ");
        double a = sc.nextDouble();
        double b = sc.nextDouble();
        double c = sc.nextDouble();

        if (a == 0) {
            // No es de segundo grado: queda bx + c = 0
            if (b == 0) {
                if (c == 0) {
                    System.out.println("Infinitas soluciones (0 = 0).");
                } else {
                    System.out.println("Sin solución.");
                }
            } else {
                System.out.printf("Ecuación de primer grado. x = %.2f%n", -c / b);
            }
        } else {
            double discriminante = b * b - 4 * a * c;
            if (discriminante > 0) {
                double x1 = (-b + Math.sqrt(discriminante)) / (2 * a);
                double x2 = (-b - Math.sqrt(discriminante)) / (2 * a);
                System.out.printf("Dos soluciones reales: x1 = %.2f, x2 = %.2f%n", x1, x2);
            } else if (discriminante == 0) {
                double x = -b / (2 * a);
                System.out.printf("Una solución doble: x = %.2f%n", x);
            } else {
                double real = -b / (2 * a);
                double imaginaria = Math.sqrt(-discriminante) / (2 * a);
                System.out.printf("Soluciones complejas: %.2f ± %.2fi%n", real, imaginaria);
            }
        }
    }
}

Cuatro ejecuciones, una por cada caso:

Coeficientes a, b y c de ax² + bx + c = 0: 1 -3 2
Dos soluciones reales: x1 = 2.00, x2 = 1.00

Coeficientes a, b y c de ax² + bx + c = 0: 1 2 1
Una solución doble: x = -1.00

Coeficientes a, b y c de ax² + bx + c = 0: 1 2 5
Soluciones complejas: -1.00 ± 2.00i

Coeficientes a, b y c de ax² + bx + c = 0: 0 2 -4
Ecuación de primer grado. x = 2.00

💡 Consejo: Aquí comparamos double con == 0 porque los coeficientes los introduce el usuario y queremos detectar exactamente el cero. Si el discriminante viniera de cálculos con decimales, sería más seguro usar un margen de tolerancia, como viste en la tabla de errores.

14.5. Calificación numérica a texto con switch

La calificación de la sección 5 también se puede resolver con un switch si antes truncas la nota a entero (int entera = (int) nota;) y agrupas valores: case 0, 1, 2, 3, 4 -> "Insuficiente"; case 5 -> "Suficiente"; ... case 9, 10 -> "Sobresaliente";. Es un truco clásico, pero ojo: 10.5 se truncaría a 10 y -0.5 a 0, así que hay que validar el rango antes con un if. Regla general: rangos → if; valores concretos → switch.


15. Ejercicios

Los seis primeros están resueltos; intenta hacerlos antes de mirar la solución. Los resultados que se muestran son de ejecuciones reales con el JDK 25.

Ejercicio 1: El mayor de tres números

Lee tres números enteros y muestra cuál es el mayor.

Ver solución
import java.util.Scanner;

public class MayorDeTres {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce tres números enteros: ");
        int a = sc.nextInt();
        int b = sc.nextInt();
        int c = sc.nextInt();

        int mayor;
        if (a >= b && a >= c) {
            mayor = a;
        } else if (b >= c) {   // aquí ya sabemos que a no es el mayor
            mayor = b;
        } else {
            mayor = c;
        }

        System.out.println("El mayor es " + mayor);
    }
}
Introduce tres números enteros: 7 12 3
El mayor es 12

En la segunda condición no hace falta comprobar b >= a: si llegamos ahí, a no era el mayor, así que el mayor es b o c. Usar >= en lugar de > hace que el programa funcione también cuando hay empates. Otra solución válida: int mayor = Math.max(a, Math.max(b, c));.

Ejercicio 2: Índice de masa corporal

Lee el peso (kg) y la altura (m), calcula el IMC (peso / altura²) con un decimal y clasifícalo según la OMS: menos de 18.5, bajo peso; hasta 25 (sin incluir), normal; hasta 30 (sin incluir), sobrepeso; 30 o más, obesidad. Valida que los datos sean positivos.

Ver solución
import java.util.Scanner;

public class Imc {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Peso (kg): ");
        double peso = sc.nextDouble();
        System.out.print("Altura (m): ");
        double altura = sc.nextDouble();

        if (peso <= 0 || altura <= 0) {
            System.out.println("Datos no válidos.");
        } else {
            double imc = peso / (altura * altura);
            String categoria;
            if (imc < 18.5) {
                categoria = "Bajo peso";
            } else if (imc < 25) {
                categoria = "Peso normal";
            } else if (imc < 30) {
                categoria = "Sobrepeso";
            } else {
                categoria = "Obesidad";
            }
            System.out.printf("IMC = %.1f -> %s%n", imc, categoria);
        }
    }
}
Peso (kg): 70
Altura (m): 1.75
IMC = 22.9 -> Peso normal

La validación va primero y el resto del programa queda en el else, así nunca se divide entre una altura de cero. La escalera usa < para que cada límite (18.5, 25, 30) pertenezca a la categoría superior, como indica la clasificación.

Ejercicio 3: Tipo de triángulo

Lee tres lados y di si forman un triángulo (cada lado debe ser positivo y menor que la suma de los otros dos). Si lo forman, indica si es equilátero, isósceles o escaleno.

Ver solución
import java.util.Scanner;

public class TipoTriangulo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce los tres lados: ");
        double a = sc.nextDouble();
        double b = sc.nextDouble();
        double c = sc.nextDouble();

        // Desigualdad triangular: cada lado menor que la suma de los otros dos
        boolean valido = a > 0 && b > 0 && c > 0
                && a < b + c && b < a + c && c < a + b;

        if (!valido) {
            System.out.println("Esos lados no forman un triángulo.");
        } else if (a == b && b == c) {
            System.out.println("Triángulo equilátero");
        } else if (a == b || a == c || b == c) {
            System.out.println("Triángulo isósceles");
        } else {
            System.out.println("Triángulo escaleno");
        }
    }
}
Introduce los tres lados: 5 5 8
Triángulo isósceles

Introduce los tres lados: 1 2 10
Esos lados no forman un triángulo.

El orden de la escalera es importante: el equilátero debe comprobarse antes que el isósceles, porque un triángulo con los tres lados iguales también cumple a == b. Guardar la validez en la variable valido hace que la escalera se lea casi como el enunciado.

Ejercicio 4: El segundo siguiente

Lee una hora en formato horas, minutos y segundos (separados por espacios), valida que sea correcta y muestra la hora un segundo después con el formato hh:mm:ss.

Ver solución
import java.util.Scanner;

public class SegundoSiguiente {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Hora (h m s): ");
        int h = sc.nextInt();
        int m = sc.nextInt();
        int s = sc.nextInt();

        if (h < 0 || h > 23 || m < 0 || m > 59 || s < 0 || s > 59) {
            System.out.println("Hora no válida.");
        } else {
            s++;
            if (s == 60) {
                s = 0;
                m++;
                if (m == 60) {
                    m = 0;
                    h++;
                    if (h == 24) {
                        h = 0;
                    }
                }
            }
            System.out.printf("Un segundo después: %02d:%02d:%02d%n", h, m, s);
        }
    }
}
Hora (h m s): 23 59 59
Un segundo después: 00:00:00

Los if anidados reflejan el «acarreo»: solo hay que mirar los minutos si los segundos han dado la vuelta, y solo las horas si lo han hecho los minutos. El formato %02d rellena con ceros a la izquierda hasta dos cifras.

Ejercicio 5: Piedra, papel o tijera

Dos jugadores escriben su jugada (piedra, papel o tijera, sin importar mayúsculas). Valida las jugadas e indica quién gana o si hay empate. Usa equals y una expresión switch con String.

Ver solución
import java.util.Scanner;

public class PiedraPapelTijera {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Jugador 1 (piedra/papel/tijera): ");
        String j1 = sc.next().toLowerCase();
        System.out.print("Jugador 2 (piedra/papel/tijera): ");
        String j2 = sc.next().toLowerCase();

        boolean validos = (j1.equals("piedra") || j1.equals("papel") || j1.equals("tijera"))
                && (j2.equals("piedra") || j2.equals("papel") || j2.equals("tijera"));

        if (!validos) {
            System.out.println("Jugada no válida.");
        } else if (j1.equals(j2)) {
            System.out.println("Empate.");
        } else {
            // ¿Qué jugada vence a la del jugador 1?
            String leGana = switch (j1) {
                case "piedra" -> "papel";
                case "papel" -> "tijera";
                default -> "piedra";     // a la tijera le gana la piedra
            };
            if (j2.equals(leGana)) {
                System.out.println("Gana el jugador 2.");
            } else {
                System.out.println("Gana el jugador 1.");
            }
        }
    }
}
Jugador 1 (piedra/papel/tijera): Tijera
Jugador 2 (piedra/papel/tijera): papel
Gana el jugador 1.

En lugar de escribir los nueve casos posibles, el switch calcula qué jugada vence a la del jugador 1. Si el jugador 2 ha sacado esa, gana él; si no (y no hay empate), gana el jugador 1. Pensar un poco antes de programar reduce mucho el código.

Ejercicio 6: Ordenar tres números

Lee tres números enteros y muéstralos ordenados de menor a mayor sin usar arrays ni métodos de ordenación, solo condicionales.

Ver solución
import java.util.Scanner;

public class OrdenarTres {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("Introduce tres números: ");
        int a = sc.nextInt();
        int b = sc.nextInt();
        int c = sc.nextInt();

        // Intercambiamos para conseguir a <= b <= c
        int aux;
        if (a > b) {        // 1) el menor de a y b queda en a
            aux = a;
            a = b;
            b = aux;
        }
        if (b > c) {        // 2) el mayor de todos queda en c
            aux = b;
            b = c;
            c = aux;
        }
        if (a > b) {        // 3) volvemos a colocar a y b
            aux = a;
            a = b;
            b = aux;
        }

        System.out.println("Ordenados: " + a + " <= " + b + " <= " + c);
    }
}
Introduce tres números: 9 2 5
Ordenados: 2 <= 5 <= 9

Para intercambiar dos variables hace falta una variable auxiliar: si hicieras a = b; b = a; perderías el valor original de a. Con tres intercambios condicionales, el mayor acaba en c y los otros dos quedan ordenados. Es la idea del algoritmo de la burbuja que verás en el módulo 08.

Ejercicio 7: Valor absoluto (propuesto)

Lee un número double y muestra su valor absoluto sin usar Math.abs. Hazlo dos veces: con if y con el operador ternario.

Ejercicio 8: Nómina con horas extra (propuesto)

Lee las horas trabajadas en una semana y el precio por hora. Las primeras 40 horas se pagan al precio normal y las que pasen de 40 se pagan un 50 % más caras. Si el sueldo bruto supera los 600 €, se retiene un 15 %; si no, un 8 %. Muestra el bruto, la retención y el neto con dos decimales.

Ejercicio 9: Fecha válida (propuesto)

Lee día, mes y año y di si la fecha es válida. Usa una expresión switch para obtener los días del mes y ten en cuenta los años bisiestos. Prueba al menos con 29/2/2024 (válida), 29/2/2025 (no válida), 31/4/2026 (no válida) y 0/1/2026 (no válida).

Ejercicio 10: Estación del año (propuesto)

Lee un mes (1-12) y un día y muestra la estación del año en el hemisferio norte, tomando como inicio de cada estación el día 21 de marzo, junio, septiembre y diciembre. Pista: primero decide por el mes con un switch y, en los meses de cambio, usa un if con el día.

Ejercicio 11: Impuesto por tramos (propuesto)

Calcula el impuesto de una renta anual con este sistema de tramos (cada porcentaje se aplica solo a la parte de la renta que cae en su tramo): hasta 12 450 €, 19 %; de 12 450 a 20 200 €, 24 %; de 20 200 a 35 200 €, 30 %; más de 35 200 €, 37 %. Por ejemplo, con 15 000 € se paga 12 450 · 0,19 + 2 550 · 0,24. Muestra el impuesto total y el tipo medio (impuesto / renta).

Ejercicio 12: Menú de conversión de unidades (propuesto)

Muestra un menú con estas opciones: 1) kilómetros a millas, 2) millas a kilómetros, 3) grados Celsius a Fahrenheit, 4) Fahrenheit a Celsius. Lee la opción y el valor, y muestra el resultado con dos decimales usando una expresión switch. Si la opción no existe, muestra un mensaje de error sin pedir el valor.


Resumen

Concepto Sintaxis / idea clave
Condición Expresión boolean: x > 0, a == b, ok && !fin
Operadores lógicos && (y), \|\| (o), ! (no); evaluación en cortocircuito
Rango x >= 1 && x <= 10 (nunca 1 <= x <= 10)
if if (cond) { ... }
if-else if (cond) { ... } else { ... }: se ejecuta exactamente uno
Escalera if ... else if ... else: la primera condición verdadera gana
Anidamiento if dentro de if; el else va con el if más cercano sin else
Bloque y ámbito Una variable vive desde su declaración hasta la } de su bloque
Ternario var = cond ? valor1 : valor2;
switch clásico case X: … break; + default:; sin break hay fallthrough
switch con flechas (Java 14) case X, Y -> ...; sin break ni fallthrough
Expresión switch int d = switch (m) { case 2 -> 28; default -> 30; };, yield en bloques, exhaustiva
Tipos en switch int, char, String, enum (y patrones desde Java 21)
Comparar String a.equals(b), a.equalsIgnoreCase(b); nunca ==
Errores típicos ; tras el if, falta de llaves, = por ==, break olvidado