Java
Tutorial Java: Estructuras de control
Java estructuras control: manejo y ejemplos. Domina las estructuras de control en Java con ejemplos prácticos y detallados.
Aprende Java y certifícateControl condicional con if, else if y else
Las estructuras de control condicional permiten ejecutar bloques de código específicos dependiendo de si se cumplen ciertas condiciones. En Java, la estructura condicional más básica y utilizada es el if
, que puede complementarse con else if
y else
para manejar múltiples condiciones.
Estructura básica del if
La sintaxis básica de una sentencia if
se compone de la palabra clave if
, seguida de una condición entre paréntesis y un bloque de código entre llaves que se ejecutará solo si la condición es verdadera:
if (condición) {
// Código que se ejecuta si la condición es verdadera
}
La condición debe ser una expresión que se evalúe a un valor booleano (true o false). Si la condición es verdadera, se ejecuta el bloque de código dentro de las llaves; si es falsa, se omite este bloque y la ejecución continúa con el código que sigue después del bloque if
.
int edad = 18;
if (edad >= 18) {
System.out.println("Eres mayor de edad");
}
En este ejemplo, el mensaje se mostrará solo si la variable edad
es mayor o igual a 18.
Uso de else
La cláusula else
se utiliza para especificar un bloque de código que se ejecutará cuando la condición del if
sea falsa:
if (condición) {
// Código que se ejecuta si la condición es verdadera
} else {
// Código que se ejecuta si la condición es falsa
}
Veamos un ejemplo práctico:
int hora = 20;
if (hora < 12) {
System.out.println("Buenos días");
} else {
System.out.println("Buenas tardes/noches");
}
En este caso, si la hora es menor que 12, se muestra "Buenos días"; de lo contrario, se muestra "Buenas tardes/noches".
Uso de else if
Cuando se necesita evaluar múltiples condiciones en secuencia, se utiliza la estructura else if
:
if (condición1) {
// Código que se ejecuta si condición1 es verdadera
} else if (condición2) {
// Código que se ejecuta si condición1 es falsa y condición2 es verdadera
} else {
// Código que se ejecuta si todas las condiciones anteriores son falsas
}
La cláusula else if
permite evaluar una nueva condición solo si las condiciones anteriores han sido falsas. Se pueden encadenar tantas cláusulas else if
como sea necesario.
int hora = 14;
if (hora < 12) {
System.out.println("Buenos días");
} else if (hora < 20) {
System.out.println("Buenas tardes");
} else {
System.out.println("Buenas noches");
}
En este ejemplo, se evalúan las condiciones en orden:
1. Si hora
es menor que 12, se muestra "Buenos días"
2. Si no se cumple lo anterior pero hora
es menor que 20, se muestra "Buenas tardes"
3. Si ninguna de las condiciones anteriores se cumple, se muestra "Buenas noches"
Condiciones anidadas
También es posible anidar estructuras condicionales, es decir, incluir una estructura if-else
dentro de otra:
int edad = 18;
boolean tieneCarnet = true;
if (edad >= 18) {
if (tieneCarnet) {
System.out.println("Puede conducir");
} else {
System.out.println("No puede conducir porque no tiene carnet");
}
} else {
System.out.println("No puede conducir porque es menor de edad");
}
En este ejemplo, primero se verifica si la persona es mayor de edad. Solo si cumple esta condición, se evalúa si tiene carnet de conducir.
Operadores lógicos en condiciones
Las condiciones pueden hacerse más complejas utilizando operadores lógicos como &&
(AND), ||
(OR) y !
(NOT):
int edad = 25;
double salario = 1500.0;
if (edad > 18 && salario >= 1000.0) {
System.out.println("Cumple los requisitos para el préstamo");
}
if (edad < 18 || salario < 1000.0) {
System.out.println("No cumple los requisitos para el préstamo");
}
if (!(edad < 18)) {
System.out.println("No es menor de edad");
}
Estos operadores permiten combinar múltiples condiciones en una sola expresión, lo que puede hacer el código más conciso.
If con una sola instrucción
Si el bloque de código a ejecutar contiene una única instrucción, las llaves son opcionales:
if (edad >= 18)
System.out.println("Eres mayor de edad");
else
System.out.println("Eres menor de edad");
Sin embargo, se recomienda siempre utilizar llaves para mejorar la legibilidad y evitar errores, especialmente cuando se modifica el código posteriormente.
Operador ternario
Java ofrece una forma abreviada de escribir ciertas estructuras if-else
mediante el operador ternario ? :
:
// Estructura if-else convencional
String mensaje;
if (edad >= 18) {
mensaje = "Mayor de edad";
} else {
mensaje = "Menor de edad";
}
// Equivalente con operador ternario
String mensaje = (edad >= 18) ? "Mayor de edad" : "Menor de edad";
El operador ternario evalúa la condición antes del signo de interrogación. Si es verdadera, devuelve el valor después del signo de interrogación; si es falsa, devuelve el valor después de los dos puntos.
Buenas prácticas
- Simplicidad: Mantener las condiciones lo más simples posible para facilitar la lectura.
- Uso de llaves: Siempre utilizar llaves, incluso para bloques de una sola instrucción.
- Orden lógico: Organizar las condiciones
else if
de manera lógica, generalmente de la más específica a la más general. - Evitar anidamiento excesivo: Demasiados niveles de anidamiento pueden hacer que el código sea difícil de leer y mantener.
// Ejemplo de código bien estructurado
int puntuación = 85;
String calificación;
if (puntuación >= 90) {
calificación = "Sobresaliente";
} else if (puntuación >= 70) {
calificación = "Notable";
} else if (puntuación >= 60) {
calificación = "Bien";
} else if (puntuación >= 50) {
calificación = "Suficiente";
} else {
calificación = "Insuficiente";
}
System.out.println("Tu calificación es: " + calificación);
En este ejemplo, las condiciones están organizadas de mayor a menor, lo que hace que el código sea claro, ya que se detiene en la primera condición que se cumple.
Casos de uso comunes
- Validación de datos: Verificar si los datos de entrada cumplen ciertos criterios.
- Control de flujo: Dirigir la ejecución del programa por diferentes caminos según las condiciones.
- Manejo de errores: Comprobar posibles errores y tomar medidas apropiadas.
// Ejemplo de validación de datos
String usuario = "admin";
String contraseña = "1234";
if (usuario == null || usuario.isEmpty()) {
System.out.println("El nombre de usuario no puede estar vacío");
} else if (contraseña == null || contraseña.length() < 6) {
System.out.println("La contraseña debe tener al menos 6 caracteres");
} else {
System.out.println("Datos válidos, procesando...");
}
Las estructuras condicionales if
, else if
y else
son fundamentales en la programación Java, ya que permiten crear programas que toman decisiones basadas en condiciones.
Control condicional con switch y switch mejorado
La estructura switch
proporciona una forma alternativa de controlar el flujo de ejecución cuando se necesita evaluar una variable contra múltiples valores posibles. A diferencia del if-else
, que evalúa expresiones booleanas, el switch
tradicional compara una expresión con varios valores constantes y ejecuta el bloque de código correspondiente al valor que coincide.
Switch tradicional
La sintaxis básica del switch
tradicional es la siguiente:
switch (expresión) {
case valor1:
// Código a ejecutar si expresión == valor1
break;
case valor2:
// Código a ejecutar si expresión == valor2
break;
// Más casos...
default:
// Código a ejecutar si ningún caso coincide
}
La expresión evaluada en el switch
debe ser de tipo byte
, short
, char
, int
, enum
, String
(desde Java 7) o sus clases envoltorio. El bloque default
es opcional y se ejecuta cuando ninguno de los casos coincide con el valor de la expresión.
Veamos un ejemplo práctico:
int dia = 3;
String nombreDia;
switch (dia) {
case 1:
nombreDia = "Lunes";
break;
case 2:
nombreDia = "Martes";
break;
case 3:
nombreDia = "Miércoles";
break;
case 4:
nombreDia = "Jueves";
break;
case 5:
nombreDia = "Viernes";
break;
case 6:
nombreDia = "Sábado";
break;
case 7:
nombreDia = "Domingo";
break;
default:
nombreDia = "Día inválido";
}
System.out.println("El día " + dia + " es " + nombreDia);
En este ejemplo, se evalúa la variable dia
y se asigna el nombre correspondiente según su valor.
La importancia del break
La palabra clave break
es crucial en el switch
tradicional. Si se omite, la ejecución "cae" al siguiente caso sin comprobar su condición, lo que se conoce como fall-through:
int opción = 2;
switch (opción) {
case 1:
System.out.println("Opción 1 seleccionada");
// Sin break, continúa al siguiente caso
case 2:
System.out.println("Opción 2 seleccionada");
// Sin break, continúa al siguiente caso
case 3:
System.out.println("Opción 3 seleccionada");
break;
default:
System.out.println("Opción no válida");
}
En este ejemplo, si opción
es 2, se imprimirán tanto "Opción 2 seleccionada" como "Opción 3 seleccionada", porque falta el break
después del caso 2.
Aunque el fall-through suele ser una fuente de errores, a veces se utiliza intencionadamente para casos que comparten el mismo código:
char calificación = 'B';
switch (calificación) {
case 'A':
case 'B':
case 'C':
System.out.println("Aprobado");
break;
case 'D':
case 'F':
System.out.println("Suspendido");
break;
default:
System.out.println("Calificación no válida");
}
En este ejemplo, las calificaciones A, B y C comparten el mismo resultado: "Aprobado".
Switch con String
Desde Java 7, se puede utilizar String
como expresión en un switch
:
String fruta = "manzana";
switch (fruta.toLowerCase()) {
case "manzana":
System.out.println("Las manzanas son rojas o verdes");
break;
case "plátano":
System.out.println("Los plátanos son amarillos");
break;
case "naranja":
System.out.println("Las naranjas son anaranjadas");
break;
default:
System.out.println("No conozco ese tipo de fruta");
}
Es importante tener en cuenta que la comparación de cadenas en un switch
distingue entre mayúsculas y minúsculas. En el ejemplo anterior, se utiliza toLowerCase()
para evitar problemas con la capitalización.
Switch con enumeraciones (enum)
El switch
es útil con enumeraciones, ya que proporciona una forma clara de manejar todos los valores posibles:
enum DiaSemana { LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO }
DiaSemana hoy = DiaSemana.MIERCOLES;
switch (hoy) {
case LUNES:
case MARTES:
case MIERCOLES:
case JUEVES:
case VIERNES:
System.out.println("Es un día laborable");
break;
case SABADO:
case DOMINGO:
System.out.println("Es fin de semana");
break;
}
Nótese que al usar enumeraciones en un switch
, no es necesario calificar los valores del caso con el nombre de la enumeración.
Switch mejorado (expresiones switch)
A partir de Java 12, se introdujo el switch mejorado o expresiones switch, que resuelve varios problemas del switch tradicional:
- Elimina la necesidad de
break
explícito - Permite que el switch devuelva un valor
- Introduce una sintaxis más concisa con el operador de flecha (
->
) - Permite agrupar múltiples casos con una sola acción
La sintaxis básica es:
// Como expresión (devuelve un valor)
tipo variable = switch (expresión) {
case valor1 -> expresión1;
case valor2 -> expresión2;
// Más casos...
default -> expresiónPorDefecto;
};
// Como sentencia (sin devolver valor)
switch (expresión) {
case valor1 -> { sentencias1; }
case valor2 -> sentencia2;
// Más casos...
default -> sentenciaPorDefecto;
}
Veamos el ejemplo de los días de la semana con el switch mejorado:
int dia = 3;
String nombreDia = switch (dia) {
case 1 -> "Lunes";
case 2 -> "Martes";
case 3 -> "Miércoles";
case 4 -> "Jueves";
case 5 -> "Viernes";
case 6 -> "Sábado";
case 7 -> "Domingo";
default -> "Día inválido";
};
System.out.println("El día " + dia + " es " + nombreDia);
Observe cómo el código es más conciso y claro. No hay break
y el valor se asigna directamente.
Múltiples casos en switch mejorado
Se pueden agrupar múltiples casos que comparten la misma acción:
DiaSemana hoy = DiaSemana.MIERCOLES;
String tipoDeJornada = switch (hoy) {
case LUNES, MARTES, MIERCOLES, JUEVES, VIERNES -> "Día laborable";
case SABADO, DOMINGO -> "Fin de semana";
};
System.out.println(hoy + " es " + tipoDeJornada);
Bloques de código en switch mejorado
Si se necesita ejecutar múltiples sentencias para un caso, se pueden utilizar bloques de código con la palabra clave yield
para devolver un valor:
int mes = 7;
String estación = switch (mes) {
case 12, 1, 2 -> {
System.out.println("Hace frío");
yield "Invierno";
}
case 3, 4, 5 -> {
System.out.println("Las flores florecen");
yield "Primavera";
}
case 6, 7, 8 -> {
System.out.println("Hace calor");
yield "Verano";
}
case 9, 10, 11 -> {
System.out.println("Las hojas caen");
yield "Otoño";
}
default -> {
System.out.println("Mes inválido");
yield "Desconocido";
}
};
System.out.println("El mes " + mes + " corresponde a " + estación);
La palabra clave yield
se utiliza para devolver un valor desde un bloque de código en una expresión switch.
Pattern matching en switch (Java 17+)
A partir de Java 17, se introdujo el pattern matching en switch
, que permite comprobar el tipo de un objeto y extraer sus componentes en una sola operación:
Object obj = "Hola mundo";
String resultado = switch (obj) {
case Integer i -> "Es un entero: " + i;
case String s -> "Es una cadena de longitud: " + s.length();
case Double d -> "Es un número decimal: " + d;
case null -> "Es null";
default -> "Es otro tipo: " + obj.getClass().getSimpleName();
};
System.out.println(resultado);
En este ejemplo, se comprueba el tipo de obj
y se extrae su valor en una variable local (i
, s
, d
) que puede utilizarse en la expresión del caso.
Pattern matching con guardas
También se pueden añadir condiciones de guarda utilizando la palabra clave when
:
Object obj = 42;
String mensaje = switch (obj) {
case String s when s.length() > 5 -> "Es una cadena larga";
case String s -> "Es una cadena corta";
case Integer i when i > 0 -> "Es un entero positivo";
case Integer i when i < 0 -> "Es un entero negativo";
case Integer i -> "Es cero";
default -> "Es otro tipo";
};
System.out.println(mensaje);
Las guardas permiten refinar aún más las condiciones de los casos.
Comparación entre if-else y switch
El switch
es más adecuado cuando:
- Se compara una única variable contra valores constantes
- Hay múltiples valores posibles para comparar
- Los casos son mutuamente excluyentes
El if-else
es preferible cuando:
- Se evalúan condiciones complejas o rangos
- Se comparan diferentes variables
- Las condiciones no son mutuamente excluyentes
// Mejor con switch
int opción = 2;
switch (opción) {
case 1 -> System.out.println("Opción 1");
case 2 -> System.out.println("Opción 2");
case 3 -> System.out.println("Opción 3");
default -> System.out.println("Otra opción");
}
// Mejor con if-else
int edad = 25;
double salario = 1500.0;
if (edad > 18 && salario >= 1000.0) {
System.out.println("Cumple los requisitos");
} else {
System.out.println("No cumple los requisitos");
}
Buenas prácticas
- Usar el switch mejorado cuando sea posible, ya que evita errores comunes y hace el código más legible.
- Incluir siempre un caso default para manejar valores inesperados.
- Evitar efectos secundarios en las expresiones de los casos.
- Preferir enumeraciones sobre constantes numéricas para mejorar la legibilidad y seguridad del código.
- Considerar el rendimiento: para un gran número de casos,
switch
puede ser más eficiente que una cadena deif-else
.
// Ejemplo de buena práctica con switch mejorado y enum
enum NivelAcceso { INVITADO, USUARIO, EDITOR, ADMINISTRADOR }
NivelAcceso nivel = NivelAcceso.EDITOR;
String mensaje = switch (nivel) {
case INVITADO -> "Acceso limitado a lectura";
case USUARIO -> "Acceso a funciones básicas";
case EDITOR -> "Puede modificar contenido";
case ADMINISTRADOR -> "Acceso completo al sistema";
};
System.out.println("Tu nivel de acceso: " + mensaje);
Control iterativo con for
Las estructuras de control iterativas, también conocidas como bucles, permiten ejecutar un bloque de código repetidamente mientras se cumpla una condición determinada. El bucle for
es una de las estructuras de iteración más utilizadas en Java, especialmente cuando se conoce de antemano el número de iteraciones a realizar.
Sintaxis básica del bucle for
La estructura básica del bucle for
consta de tres partes principales:
for (inicialización; condición; actualización) {
// Código a ejecutar en cada iteración
}
- Inicialización: Se ejecuta una sola vez al principio y generalmente se utiliza para declarar e inicializar una variable de control.
- Condición: Se evalúa antes de cada iteración. Si es
true
, se ejecuta el bloque de código; si esfalse
, el bucle termina. - Actualización: Se ejecuta después de cada iteración y normalmente se utiliza para modificar la variable de control.
Veamos un ejemplo básico:
for (int i = 0; i < 5; i++) {
System.out.println("Iteración número: " + i);
}
En este ejemplo:
1. Se inicializa i
con el valor 0
2. Se verifica si i
es menor que 5
3. Si la condición es verdadera, se ejecuta el bloque de código
4. Después de la ejecución, se incrementa i
en 1
5. Se vuelve al paso 2 y se repite el proceso hasta que i
ya no sea menor que 5
La salida sería:
Iteración número: 0
Iteración número: 1
Iteración número: 2
Iteración número: 3
Iteración número: 4
Variaciones del bucle for
Decrementos en lugar de incrementos
Se puede iterar en orden descendente utilizando decrementos:
for (int i = 10; i > 0; i--) {
System.out.println("Cuenta regresiva: " + i);
}
System.out.println("¡Despegue!");
Incrementos/decrementos diferentes a 1
No es necesario incrementar o decrementar de uno en uno:
// Mostrar los números pares del 0 al 20
for (int i = 0; i <= 20; i += 2) {
System.out.print(i + " ");
}
// Salida: 0 2 4 6 8 10 12 14 16 18 20
Múltiples variables de control
Se pueden utilizar múltiples variables en un bucle for
:
for (int i = 1, j = 10; i <= 10 && j >= 1; i++, j--) {
System.out.println("i = " + i + ", j = " + j);
}
En este ejemplo, i
se incrementa mientras que j
se decrementa en cada iteración.
Omitir partes del for
Cualquiera de las tres partes del bucle for
puede omitirse, aunque los puntos y comas son obligatorios:
// Inicialización fuera del bucle
int i = 0;
for (; i < 5; i++) {
System.out.println(i);
}
// Actualización dentro del bloque
for (int j = 0; j < 5;) {
System.out.println(j);
j++;
}
// Bucle infinito (la condición siempre es verdadera)
for (;;) {
System.out.println("Bucle infinito");
// Necesita una condición de salida interna
break; // Sale del bucle
}
Bucle for-each (enhanced for)
Java ofrece una versión mejorada del bucle for
, conocida como bucle for-each, que simplifica la iteración sobre colecciones y arrays:
for (tipo nombreVariable : colección) {
// Código a ejecutar para cada elemento
}
Este tipo de bucle es más conciso y menos propenso a errores cuando solo se necesita acceder a los elementos sin modificar la colección:
String[] frutas = {"Manzana", "Plátano", "Naranja", "Fresa"};
// Bucle for tradicional
for (int i = 0; i < frutas.length; i++) {
System.out.println("Me gusta la " + frutas[i]);
}
// Bucle for-each (más simple)
for (String fruta : frutas) {
System.out.println("Me gusta la " + fruta);
}
El bucle for-each es especialmente útil con colecciones como ArrayList
, Set
, etc.:
import java.util.List;
import java.util.ArrayList;
List<Integer> números = new ArrayList<>();
números.add(1);
números.add(2);
números.add(3);
for (Integer número : números) {
System.out.println("Número: " + número);
}
Limitaciones del bucle for-each
Aunque el bucle for-each es más simple, tiene algunas limitaciones:
- No permite modificar la colección durante la iteración
- No proporciona acceso al índice del elemento actual
- No permite iterar en orden inverso fácilmente
- No permite iterar sobre múltiples colecciones simultáneamente
// Si necesitas el índice, usa el for tradicional
String[] nombres = {"Ana", "Carlos", "Elena"};
for (int i = 0; i < nombres.length; i++) {
System.out.println("Posición " + i + ": " + nombres[i]);
}
Control de flujo dentro de bucles
Dentro de cualquier bucle, incluido el for
, se pueden utilizar las instrucciones break
y continue
para controlar el flujo de ejecución:
- break: Termina el bucle inmediatamente y continúa con el código después del bucle.
- continue: Salta a la siguiente iteración del bucle, omitiendo el resto del código en la iteración actual.
// Uso de break
for (int i = 1; i <= 10; i++) {
if (i == 5) {
System.out.println("Encontrado el 5, saliendo del bucle");
break;
}
System.out.println("Número: " + i);
}
// Uso de continue
for (int i = 1; i <= 10; i++) {
if (i % 2 == 0) {
// Salta los números pares
continue;
}
System.out.println("Número impar: " + i);
}
Bucles anidados
Se pueden anidar bucles for
para trabajar con estructuras de datos multidimensionales o para resolver problemas más complejos:
// Tabla de multiplicar
for (int i = 1; i <= 10; i++) {
for (int j = 1; j <= 10; j++) {
System.out.printf("%4d", i * j);
}
System.out.println(); // Nueva línea después de cada fila
}
Este código genera una tabla de multiplicar del 1 al 10. El bucle externo controla las filas, mientras que el bucle interno controla las columnas.
Etiquetas en bucles
Java permite etiquetar bucles, lo que es útil cuando se trabaja con bucles anidados y se necesita salir de un bucle específico:
externo: for (int i = 0; i < 5; i++) {
interno: for (int j = 0; j < 5; j++) {
if (i * j > 6) {
System.out.println("Saliendo del bucle externo cuando i=" + i + ", j=" + j);
break externo; // Sale del bucle etiquetado como "externo"
}
System.out.println("i=" + i + ", j=" + j);
}
}
En este ejemplo, cuando el producto i * j
supera 6, se utiliza break externo
para salir completamente del bucle externo, no solo del bucle interno.
Casos de uso comunes
Iteración sobre arrays
int[] números = {10, 20, 30, 40, 50};
int suma = 0;
for (int número : números) {
suma += número;
}
System.out.println("La suma es: " + suma);
Generación de patrones
// Patrón de triángulo
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("* ");
}
System.out.println();
}
Salida:
*
* *
* * *
* * * *
* * * * *
Búsqueda en arrays
int[] valores = {15, 7, 23, 9, 4, 12};
int buscar = 9;
boolean encontrado = false;
for (int i = 0; i < valores.length; i++) {
if (valores[i] == buscar) {
System.out.println("Valor encontrado en la posición: " + i);
encontrado = true;
break;
}
}
if (!encontrado) {
System.out.println("Valor no encontrado");
}
Buenas prácticas
- Nombres descriptivos: Utilizar nombres significativos para las variables de control.
- Simplicidad: Mantener el cuerpo del bucle lo más simple posible.
- Evitar modificaciones: No modificar la variable de control dentro del cuerpo del bucle.
- Preferir for-each: Usar el bucle for-each cuando sea posible para mayor claridad.
- Verificar límites: Asegurarse de que los índices no excedan los límites del array.
// Mal ejemplo
for (int i = 0; i < array.length; i++) {
// Modificar i dentro del bucle puede causar comportamientos inesperados
if (condición) i += 2;
}
// Buen ejemplo
for (int índice = 0; índice < array.length; índice++) {
// Cuerpo del bucle sin modificar índice
}
Control iterativo con while y do while
Los bucles while
y do-while
son estructuras de control iterativas que permiten ejecutar un bloque de código repetidamente mientras se cumpla una condición específica. A diferencia del bucle for
, que se utiliza principalmente cuando se conoce el número de iteraciones de antemano, los bucles while
y do-while
son más adecuados cuando el número de iteraciones depende de una condición que puede cambiar durante la ejecución.
Bucle while
El bucle while
evalúa una condición antes de ejecutar el bloque de código. Si la condición es verdadera, se ejecuta el bloque y luego se vuelve a evaluar la condición. Este proceso se repite hasta que la condición se vuelve falsa.
La sintaxis básica es:
while (condición) {
// Código a ejecutar mientras la condición sea verdadera
}
Veamos un ejemplo sencillo:
int contador = 1;
while (contador <= 5) {
System.out.println("Contador: " + contador);
contador++;
}
En este ejemplo:
1. Se inicializa contador
con el valor 1
2. Se verifica si contador
es menor o igual a 5
3. Si la condición es verdadera, se ejecuta el bloque de código
4. Se incrementa contador
en 1
5. Se vuelve al paso 2 y se repite el proceso hasta que contador
ya no sea menor o igual a 5
La salida sería:
Contador: 1
Contador: 2
Contador: 3
Contador: 4
Contador: 5
Características importantes del bucle while
- La condición se evalúa antes de cada iteración
- Si la condición es falsa desde el principio, el bloque de código no se ejecuta ni una sola vez
- Es necesario modificar alguna variable dentro del bucle para que la condición eventualmente se vuelva falsa, de lo contrario se crea un bucle infinito
// Ejemplo de bucle infinito (¡cuidado!)
while (true) {
System.out.println("Este mensaje se imprimirá indefinidamente");
// Necesita una condición de salida interna
if (algúnaCriterio) {
break; // Sale del bucle
}
}
Bucle do-while
El bucle do-while
es similar al while
, pero con una diferencia crucial: la condición se evalúa después de ejecutar el bloque de código, lo que garantiza que el bloque se ejecute al menos una vez, incluso si la condición es falsa desde el principio.
La sintaxis básica es:
do {
// Código a ejecutar al menos una vez
} while (condición);
Veamos un ejemplo:
int contador = 1;
do {
System.out.println("Contador: " + contador);
contador++;
} while (contador <= 5);
La salida sería la misma que en el ejemplo anterior:
Contador: 1
Contador: 2
Contador: 3
Contador: 4
Contador: 5
Pero observemos qué sucede si la condición es falsa desde el principio:
int contador = 10;
// Bucle while
while (contador <= 5) {
System.out.println("Este mensaje nunca se imprimirá");
contador++;
}
// Bucle do-while
do {
System.out.println("Este mensaje se imprimirá una vez");
contador++;
} while (contador <= 5);
En este caso, el bucle while
no se ejecuta ni una sola vez porque la condición es falsa desde el principio. Sin embargo, el bucle do-while
se ejecuta una vez antes de evaluar la condición.
Casos de uso para while
El bucle while
es especialmente útil en situaciones donde:
- No se conoce el número exacto de iteraciones de antemano
- La iteración depende de una condición externa o de entrada del usuario
- Se necesita procesar datos hasta encontrar un valor específico
// Leer entrada del usuario hasta que ingrese "salir"
Scanner scanner = new Scanner(System.in);
String entrada = "";
while (!entrada.equalsIgnoreCase("salir")) {
System.out.print("Ingrese un comando (o 'salir' para terminar): ");
entrada = scanner.nextLine();
if (!entrada.equalsIgnoreCase("salir")) {
System.out.println("Procesando: " + entrada);
}
}
System.out.println("Programa terminado");
Casos de uso para do-while
El bucle do-while
es ideal cuando:
- Se necesita ejecutar el código al menos una vez, independientemente de la condición
- Se valida la entrada del usuario después de solicitarla
- Se implementan menús de opciones que deben mostrarse al menos una vez
// Menú de opciones
Scanner scanner = new Scanner(System.in);
int opción;
do {
System.out.println("\n--- MENÚ PRINCIPAL ---");
System.out.println("1. Ver datos");
System.out.println("2. Agregar nuevo registro");
System.out.println("3. Modificar registro");
System.out.println("4. Eliminar registro");
System.out.println("0. Salir");
System.out.print("Seleccione una opción: ");
opción = scanner.nextInt();
switch (opción) {
case 1 -> System.out.println("Mostrando datos...");
case 2 -> System.out.println("Agregando nuevo registro...");
case 3 -> System.out.println("Modificando registro...");
case 4 -> System.out.println("Eliminando registro...");
case 0 -> System.out.println("Saliendo del programa...");
default -> System.out.println("Opción no válida, intente de nuevo");
}
} while (opción != 0);
Control de flujo dentro de bucles while y do-while
Al igual que en el bucle for
, se pueden utilizar las instrucciones break
y continue
para controlar el flujo de ejecución:
int i = 1;
while (i <= 10) {
if (i == 5) {
System.out.println("Saltando el número 5");
i++;
continue; // Salta a la siguiente iteración
}
if (i == 8) {
System.out.println("Encontrado el número 8, saliendo del bucle");
break; // Sale del bucle
}
System.out.println("Número: " + i);
i++;
}
Bucles anidados
También se pueden anidar bucles while
y do-while
, al igual que los bucles for
:
int i = 1;
while (i <= 3) {
int j = 1;
while (j <= 3) {
System.out.print("(" + i + "," + j + ") ");
j++;
}
System.out.println(); // Nueva línea después de cada fila
i++;
}
Salida:
(1,1) (1,2) (1,3)
(2,1) (2,2) (2,3)
(3,1) (3,2) (3,3)
Comparación entre while, do-while y for
Cada tipo de bucle tiene sus propias ventajas y casos de uso ideales:
Bucle for: Ideal cuando se conoce el número de iteraciones de antemano.
Bucle while: Mejor cuando la condición de terminación depende de factores que pueden cambiar durante la ejecución.
Bucle do-while: Preferible cuando el bloque debe ejecutarse al menos una vez antes de evaluar la condición.
Ejemplos prácticos
Validación de entrada con do-while
Scanner scanner = new Scanner(System.in);
int número;
do {
System.out.print("Ingrese un número positivo: ");
número = scanner.nextInt();
if (número <= 0) {
System.out.println("Error: El número debe ser positivo");
}
} while (número <= 0);
System.out.println("Número válido ingresado: " + número);
Búsqueda en una lista con while
List<String> nombres = List.of("Ana", "Carlos", "Elena", "David", "Beatriz");
String buscar = "Elena";
boolean encontrado = false;
int índice = 0;
while (índice < nombres.size() && !encontrado) {
if (nombres.get(índice).equals(buscar)) {
encontrado = true;
} else {
índice++;
}
}
if (encontrado) {
System.out.println("Nombre encontrado en la posición: " + índice);
} else {
System.out.println("Nombre no encontrado");
}
Algoritmo de adivinanza con while
Scanner scanner = new Scanner(System.in);
Random random = new Random();
int númeroSecreto = random.nextInt(100) + 1; // Número entre 1 y 100
int intento;
int intentos = 0;
boolean adivinado = false;
System.out.println("Adivina el número entre 1 y 100");
while (!adivinado) {
System.out.print("Ingresa tu intento: ");
intento = scanner.nextInt();
intentos++;
if (intento < númeroSecreto) {
System.out.println("El número es mayor");
} else if (intento > númeroSecreto) {
System.out.println("El número es menor");
} else {
adivinado = true;
System.out.println("¡Correcto! Has adivinado en " + intentos + " intentos");
}
}
Buenas prácticas
- Evitar bucles infinitos: Asegurarse de que la condición eventualmente se vuelva falsa.
- Inicialización adecuada: Inicializar las variables antes de usarlas en la condición del bucle.
- Actualización clara: Actualizar las variables de control de manera clara y consistente.
- Condiciones simples: Mantener las condiciones del bucle lo más simples posible.
- Elegir el bucle adecuado: Utilizar
for
cuando se conoce el número de iteraciones,while
cuando depende de una condición, ydo-while
cuando el bloque debe ejecutarse al menos una vez.
// Mal ejemplo: Posible bucle infinito
int contador = 0;
while (contador < 10) {
System.out.println(contador);
// Se olvidó incrementar contador
}
// Buen ejemplo
int contador = 0;
while (contador < 10) {
System.out.println(contador);
contador++; // Asegura que el bucle terminará
}
Consideraciones de rendimiento
- Los bucles
while
ydo-while
pueden ser ligeramente más eficientes que el buclefor
cuando la condición de terminación es simple y no requiere inicialización o actualización adicional. - Sin embargo, la diferencia de rendimiento suele ser insignificante en la mayoría de los casos, por lo que la elección del tipo de bucle debe basarse principalmente en la claridad y la adecuación al problema.
// Comparación de rendimiento (diferencia mínima)
long inicio, fin;
// Bucle for
inicio = System.nanoTime();
for (int i = 0; i < 1000000; i++) {
// Operación
}
fin = System.nanoTime();
System.out.println("Tiempo con for: " + (fin - inicio) + " ns");
// Bucle while
inicio = System.nanoTime();
int j = 0;
while (j < 1000000) {
// Misma operación
j++;
}
fin = System.nanoTime();
System.out.println("Tiempo con while: " + (fin - inicio) + " ns");
do {
// Solicitar entrada
// Procesar entrada
} while (!entradaVálida);
while (!condiciónDeSalida) {
// Procesar algo
// Actualizar condiciónDeSalida
}
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
Ejercicios de esta lección Estructuras de control
Evalúa tus conocimientos de esta lección Estructuras de control con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Clases abstractas
Listas
Métodos de la clase String
Streams: reduce()
Polimorfismo
Pattern Matching
Streams: flatMap()
Llamada y sobrecarga de funciones
Métodos referenciados
Métodos de la clase String
Representación de Fecha
Operadores lógicos
Inferencia de tipos con var
Tipos de datos
Estructuras de iteración
Streams: forEach()
Objetos
Funciones lambda
Uso de Scanner
CRUD en Java de modelo Customer sobre un ArrayList
Tipos de variables
Streams: collect()
Operadores aritméticos
Arrays y matrices
Clases y objetos
Interfaz funcional Consumer
Interfaces
Enumeraciones Enums
API java.nio 2
API Optional
Interfaz funcional Function
Encapsulación
Interfaces
Uso de API Optional
Representación de Hora
Herencia básica
Clases y objetos
Interfaz funcional Supplier
HashMap
Sobrecarga de métodos
Polimorfismo de tiempo de ejecución
OOP en Java
Sobrecarga de métodos
Clases sealed
Creación de Streams
Records
Encapsulación
Streams: min max
Métodos avanzados de la clase String
Funciones
Polimorfismo de tiempo de compilación
Reto sintaxis Java
Conjuntos
Estructuras de control
Recursión
Excepciones
Herencia avanzada
Estructuras de selección
Uso de interfaces
Operadores
Variables
HashSet
Objeto Scanner
Streams: filter()
Operaciones de Streams
Interfaz funcional Predicate
Streams: sorted()
Configuración de entorno
CRUD en Java de modelo Customer sobre un HashMap
Uso de variables
Clases
Streams: distinct()
Streams: count()
ArrayList
Datos de referencia
Interfaces funcionales
Métodos básicos de la clase String
Tipos de datos
Clases abstractas
Instalación
Funciones
Excepciones
Estructuras de control
Herencia de clases
La clase Scanner
Generics
Streams: map()
Funciones y encapsulamiento
Streams: match
Gestión de errores y excepciones
Datos primitivos
Todas las lecciones de Java
Accede a todas las lecciones de Java y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Instalación De Java
Introducción Y Entorno
Configuración De Entorno Java
Introducción Y Entorno
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Recursión
Sintaxis
Excepciones
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Clases Abstractas
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Sobrecarga De Métodos
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
La Clase Scanner
Programación Orientada A Objetos
Métodos De La Clase String
Programación Orientada A Objetos
Records
Programación Orientada A Objetos
Pattern Matching
Programación Orientada A Objetos
Inferencia De Tipos Con Var
Programación Orientada A Objetos
Enumeraciones Enums
Programación Orientada A Objetos
Generics
Programación Orientada A Objetos
Clases Sealed
Programación Orientada A Objetos
Listas
Framework Collections
Conjuntos
Framework Collections
Mapas
Framework Collections
Funciones Lambda
Programación Funcional
Interfaz Funcional Consumer
Programación Funcional
Interfaz Funcional Predicate
Programación Funcional
Interfaz Funcional Supplier
Programación Funcional
Interfaz Funcional Function
Programación Funcional
Métodos Referenciados
Programación Funcional
Creación De Streams
Programación Funcional
Operaciones Intermedias Con Streams: Map()
Programación Funcional
Operaciones Intermedias Con Streams: Filter()
Programación Funcional
Operaciones Intermedias Con Streams: Distinct()
Programación Funcional
Operaciones Finales Con Streams: Collect()
Programación Funcional
Operaciones Finales Con Streams: Min Max
Programación Funcional
Operaciones Intermedias Con Streams: Flatmap()
Programación Funcional
Operaciones Intermedias Con Streams: Sorted()
Programación Funcional
Operaciones Finales Con Streams: Reduce()
Programación Funcional
Operaciones Finales Con Streams: Foreach()
Programación Funcional
Operaciones Finales Con Streams: Count()
Programación Funcional
Operaciones Finales Con Streams: Match
Programación Funcional
Api Optional
Programación Funcional
Api Java.nio 2
Entrada Y Salida (Io)
Api Java.time
Api Java.time
Ecosistema Jakarta Ee De Java
Frameworks Para Java
Certificados de superación de Java
Supera todos los ejercicios de programación del curso de Java y obtén certificados de superación para mejorar tu currículum y tu empleabilidad.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la estructura básica de una sentencia
if
- Aprender a emplear
else if
yelse
para manejar múltiples condiciones - Saber cuándo usar operadores lógicos dentro de las condiciones
- Conocer el uso del operador ternario y sus ventajas
- Aplicar buenas prácticas para mejorar la claridad y eficiencia del código