Aprender estructuras de control en Java es uno de esos pasos que marcan un antes y un después cuando empiezas a programar. Al principio, un programa puede parecer una simple lista de instrucciones que se ejecutan de arriba abajo. Pero en cuanto quieres que el programa tome decisiones, repita tareas, valide datos, controle errores o se pueda depurar con método, necesitas dominar las estructuras de control en Java.
En mi caso, tanto en proyectos reales como explicando programación en el aula, suelo insistir en una idea: programar no consiste en memorizar if, for o try catch, sino en entender qué camino sigue el programa y por qué. Una estructura de control bien elegida hace que el código se lea casi como una explicación del problema.
Las estructuras de control en Java permiten decidir qué instrucciones se ejecutan, cuántas veces se repiten, cuándo se interrumpe un flujo y cómo debe reaccionar el programa ante situaciones inesperadas. En el documento base de este artículo, el tema se organiza precisamente alrededor del flujo de ejecución, las estructuras de selección, las estructuras de repetición, las sentencias de salto, el control de excepciones, la depuración y la documentación de programas.
A lo largo de esta guía vamos a ver las estructuras de control en Java desde un punto de vista práctico, con ejemplos sencillos, errores frecuentes y recomendaciones para elegir la estructura adecuada en cada situación.
Qué son las estructuras de control en Java
Las estructuras de control en Java son instrucciones que permiten modificar el flujo normal de ejecución de un programa. Sin ellas, Java ejecutaría las líneas de código una tras otra, siempre en el mismo orden y sin reaccionar ante condiciones, repeticiones o errores.
Por ejemplo, un programa lineal podría ser algo así:
System.out.println("Inicio");
System.out.println("Proceso");
System.out.println("Fin");
Este código siempre hace lo mismo. No pregunta nada, no decide nada, no repite nada y no controla errores. Es útil para entender el orden básico de ejecución, pero se queda corto en cuanto queremos crear programas con comportamiento real.
En cambio, con estructuras de control en Java podemos hacer cosas como:
if (edad >= 18) {
System.out.println("Puedes acceder");
} else {
System.out.println("No puedes acceder todavía");
}
Aquí el programa ya no ejecuta siempre el mismo camino. Depende del valor de edad. Esa es la esencia del control de flujo: el programa observa una condición y actúa en consecuencia.
El flujo de ejecución: de arriba abajo… hasta que el programa decide algo
El flujo de ejecución es el orden en el que se ejecutan las instrucciones. En Java, ese flujo suele avanzar de arriba abajo, pero las estructuras de control en Java permiten cambiarlo.
Podemos agruparlas así:
| Necesidad del programa | Estructura habitual |
|---|---|
| Elegir entre varios caminos | if, else, switch |
| Repetir instrucciones | while, do while, for |
| Interrumpir o modificar el flujo | break, continue, return |
| Evitar que un error cierre el programa | try, catch, finally |
| Localizar por qué falla el programa | depuración, puntos de ruptura, inspección de variables |
Esta clasificación aparece también en el documento de partida: selección para elegir caminos, repetición para ejecutar bloques varias veces, sentencias de salto para modificar el flujo, excepciones para controlar errores y depuración para localizar fallos.
En clase suelo verlo con un ejemplo muy simple: un menú. Si el usuario pulsa 1, el programa hace una cosa; si pulsa 2, hace otra; si pulsa 0, termina. Ese pequeño menú ya combina varias estructuras de control en Java: un bucle para repetir, un switch para elegir opción y quizá un try catch para evitar que el programa se rompa si el usuario escribe texto donde se esperaba un número.
Por qué las condiciones booleanas son la base de casi todo
Antes de entender bien las estructuras de control en Java, conviene dominar las expresiones booleanas. Una expresión booleana solo puede tener dos resultados: true o false.
Algunos ejemplos:
edad >= 18
nota >= 5
opcion != 0
usuario.equals("admin")
saldo > 0 && activo
Todas esas expresiones responden a una pregunta:
- ¿La edad es mayor o igual que 18?
- ¿La nota es mayor o igual que 5?
- ¿La opción es distinta de 0?
- ¿El usuario se llama
"admin"? - ¿El saldo es positivo y la cuenta está activa?
Java necesita que las condiciones dentro de un if, un while o un do while produzcan un valor booleano. No basta con poner una variable numérica y esperar que Java la interprete como verdadera o falsa. Esta es una diferencia importante respecto a otros lenguajes.
Por eso, cuando explico estructuras de control en Java, siempre empiezo por aquí. Si no entiendes bien las condiciones, los if y los bucles se convierten en una especie de magia rara. Y programar no debería sentirse como magia: debería sentirse como una cadena de decisiones que puedes seguir paso a paso.
Operadores relacionales y lógicos que conviene dominar desde el principio
Las condiciones se construyen con operadores relacionales y operadores lógicos.
Los operadores relacionales comparan valores:
opcion == 1
opcion != 0
edad > 18
temperatura < 0
nota >= 5
intentos <= 3
Los operadores lógicos combinan condiciones:
edad >= 18 && tieneEntrada
esAdmin || esProfesor
!activo
El operador && significa “y”: deben cumplirse las dos condiciones. El operador || significa “o”: basta con que se cumpla una de ellas. El operador ! niega una condición.
Un error clásico al empezar con estructuras de control en Java es confundir = con ==.
int opcion = 1; // asigna un valor
opcion == 1 // compara un valor
El operador = sirve para asignar. El operador == sirve para comparar. Esta diferencia parece pequeña, pero puede provocar errores muy molestos.
Estructuras de selección: cómo tomar decisiones en Java
Las estructuras de selección son las estructuras de control en Java que permiten elegir entre distintos caminos. Se usan para validar datos, clasificar valores, mostrar mensajes diferentes o ejecutar una acción solo cuando se cumple una condición.
En la práctica, las más importantes son:
ifif elseelse ifswitch- operador ternario
Cuando vienes de proyectos reales, aprendes rápido que elegir bien la estructura no es solo una cuestión de sintaxis. También afecta a la claridad del código. No es lo mismo resolver un menú con diez if desordenados que usar un switch claro y fácil de leer.
if: ejecutar código solo si se cumple una condición
La estructura if ejecuta un bloque de código únicamente si la condición es verdadera.
int edad = 20;
if (edad >= 18) {
System.out.println("Puedes acceder.");
}
Este ejemplo es sencillo: si edad es mayor o igual que 18, se muestra el mensaje. Si no, el programa continúa después del bloque.
El if es una de las estructuras de control en Java más básicas y más usadas. Sirve cuando quieres que algo ocurra solo bajo una condición concreta.
Ejemplos típicos:
if (nota < 5) {
System.out.println("Debes recuperar.");
}
if (saldo <= 0) {
System.out.println("Saldo insuficiente.");
}
if (opcion == 0) {
System.out.println("Saliendo...");
}
Mi recomendación es que el if sea fácil de leer. Una condición como esta se entiende bien:
if (nota >= 5 && nota <= 10) {
System.out.println("Nota válida.");
}
Pero si empiezas a meter demasiadas condiciones en la misma línea, quizá convenga separar la lógica o usar variables intermedias.
if else: elegir entre dos caminos
La estructura if else permite ejecutar un bloque si la condición es verdadera y otro bloque si es falsa.
int edad = 16;
if (edad >= 18) {
System.out.println("Puedes acceder.");
} else {
System.out.println("No puedes acceder todavía.");
}
Este patrón es perfecto cuando hay dos caminos excluyentes. Por ejemplo:
double nota = 6.5;
if (nota >= 5) {
System.out.println("Aprobado");
} else {
System.out.println("Suspenso");
}
Aquí no hay duda: una nota no puede estar aprobada y suspensa al mismo tiempo. Por eso if else encaja tan bien.
Dentro de las estructuras de control en Java, if else es una de las más útiles para introducir lógica real en programas sencillos. Clasificar edades, validar notas, comprobar accesos o decidir si una operación puede realizarse son casos muy habituales.
else if: clasificar valores en varios casos
Cuando hay más de dos posibilidades, podemos encadenar if, else if y else.
double nota = 7.5;
if (nota < 5) {
System.out.println("Insuficiente");
} else if (nota < 6) {
System.out.println("Suficiente");
} else if (nota < 7) {
System.out.println("Bien");
} else if (nota < 9) {
System.out.println("Notable");
} else {
System.out.println("Sobresaliente");
}
Esta estructura es muy útil para clasificar valores por rangos. El orden importa mucho, porque Java evalúa las condiciones de arriba abajo y entra en el primer bloque cuya condición se cumpla.
Este detalle es importante: si las condiciones están mal ordenadas, el programa puede funcionar, pero dar resultados incorrectos. Y esos son los errores más peligrosos para quien empieza, porque el programa no siempre se rompe. A veces simplemente calcula mal.
Por eso insisto mucho en probar casos límite. En el ejemplo de notas, habría que probar valores como 4.9, 5, 6, 7, 9 y 10.
switch: cuando comparas una opción con valores concretos
La estructura switch permite elegir un bloque de código según el valor de una variable o expresión. Suele usarse cuando comparamos una misma variable con varios valores concretos.
int opcion = 2;
switch (opcion) {
case 1:
System.out.println("Crear usuario");
break;
case 2:
System.out.println("Modificar usuario");
break;
case 3:
System.out.println("Eliminar usuario");
break;
default:
System.out.println("Opción no válida");
}
El switch encaja especialmente bien con menús, códigos, opciones numéricas o valores cerrados. El documento lo presenta como una estructura adecuada cuando se compara una misma variable con valores concretos, como opciones de menú, códigos o días de la semana.
Dentro de las estructuras de control en Java, switch ayuda mucho a ordenar el código cuando hay muchas opciones. En lugar de escribir varios if else if, podemos mostrar claramente qué ocurre en cada caso.
Un error frecuente es olvidar el break en un switch clásico. Si no lo pones, Java puede seguir ejecutando los siguientes case. Este comportamiento se llama fall-through y suele dar bastantes quebraderos de cabeza al principio.
switch (opcion) {
case 1:
System.out.println("Opción 1");
case 2:
System.out.println("Opción 2");
}
Si opcion vale 1, este código puede imprimir también "Opción 2" porque falta break.
Operador ternario: útil, pero solo si mejora la legibilidad
El operador ternario permite escribir una selección simple en una sola expresión.
int edad = 19;
String mensaje = edad >= 18 ? "Mayor de edad" : "Menor de edad";
System.out.println(mensaje);
Su forma general es:
condicion ? valorSiVerdadero : valorSiFalso
Es cómodo para asignaciones sencillas. Por ejemplo:
String resultado = nota >= 5 ? "Aprobado" : "Suspenso";
Pero no conviene abusar. Si la condición es larga o hay varias decisiones, un if else suele ser más claro. Las estructuras de control en Java deben ayudar a entender el código, no convertirlo en un acertijo.
Error típico: comparar textos con == en lugar de equals()
En Java, comparar textos con == es uno de los errores más habituales al empezar.
String usuario = "admin";
if (usuario == "admin") {
System.out.println("Usuario administrador");
}
Puede parecer que funciona en algunos casos, pero no es la forma correcta de comparar contenido de cadenas.
La forma correcta es usar equals():
if (usuario.equals("admin")) {
System.out.println("Usuario administrador");
}
O equalsIgnoreCase() si queremos ignorar mayúsculas y minúsculas:
if (usuario.equalsIgnoreCase("ADMIN")) {
System.out.println("Coincide aunque cambien las mayúsculas");
}
La razón es que == compara referencias cuando trabajamos con objetos, mientras que equals() compara el contenido. El documento destaca este punto como un detalle importante para evitar errores lógicos difíciles de detectar.
Estructuras de repetición: cómo repetir instrucciones sin copiar código
Las estructuras de repetición son las estructuras de control en Java que permiten ejecutar varias veces un bloque de instrucciones. También se llaman bucles.
Son imprescindibles para:
- recorrer listas de datos;
- repetir menús;
- validar entradas;
- calcular sumas o medias;
- procesar varios valores;
- ejecutar una acción hasta que se cumpla una condición de parada.
Una idea clave: todo bucle necesita una condición de continuidad o de parada. Si esa condición nunca cambia, puedes crear un bucle infinito.
while: repetir mientras se cumpla una condición
El bucle while evalúa la condición antes de ejecutar el bloque.
int contador = 1;
while (contador <= 5) {
System.out.println("Vuelta " + contador);
contador++;
}
Primero comprueba si contador <= 5. Si la condición es verdadera, entra en el bucle. Al final de cada vuelta, contador++ aumenta el valor de la variable. Cuando contador pasa de 5, el bucle termina.
El while es útil cuando no sabes de antemano cuántas veces se va a repetir algo.
Por ejemplo, leer notas hasta que el usuario introduzca -1:
Scanner sc = new Scanner(System.in);
double nota;
System.out.print("Introduce una nota (-1 para salir): ");
nota = sc.nextDouble();
while (nota != -1) {
System.out.println("Nota registrada: " + nota);
System.out.print("Introduce otra nota (-1 para salir): ");
nota = sc.nextDouble();
}
sc.close();
Este patrón se entiende muy bien en clase porque refleja una situación real: “seguir pidiendo datos hasta que el usuario decida terminar”.
do while: ideal para menús que deben mostrarse al menos una vez
El bucle do while ejecuta el bloque al menos una vez y comprueba la condición al final.
int opcion;
Scanner sc = new Scanner(System.in);
do {
System.out.println("1. Saludar");
System.out.println("0. Salir");
System.out.print("Elige una opción: ");
opcion = sc.nextInt();
if (opcion == 1) {
System.out.println("Hola");
}
} while (opcion != 0);
sc.close();
Este tipo de bucle encaja especialmente bien con menús. Primero muestras las opciones y después decides si repites o sales.
Dentro de las estructuras de control en Java, do while tiene una ventaja clara: garantiza que el bloque se ejecuta al menos una vez. Eso lo hace muy natural para programas de consola.
En proyectos sencillos, un menú con do while y switch suele ser una de las mejores prácticas para empezar a combinar estructuras de control en Java.
for: cuando sabes cuántas vueltas necesitas
El bucle for se usa normalmente cuando sabes cuántas veces quieres repetir una acción.
for (int i = 1; i <= 5; i++) {
System.out.println("Vuelta " + i);
}
En una sola línea agrupa tres partes:
for (inicialización; condición; actualización)
Ejemplo:
for (int i = 1; i <= 10; i++) {
System.out.println("Número: " + i);
}
El for es ideal para tablas de multiplicar:
Scanner sc = new Scanner(System.in);
System.out.print("Introduce un número: ");
int numero = sc.nextInt();
for (int i = 1; i <= 10; i++) {
System.out.println(numero + " x " + i + " = " + (numero * i));
}
sc.close();
En mi experiencia, el for suele ser el bucle que más rápido se entiende cuando hay un contador visible. El problema llega cuando se usa para todo. Si no sabes cuántas vueltas habrá, quizá un while sea más adecuado.
Contadores, acumuladores y centinelas
Muchos programas con estructuras de control en Java se apoyan en tres patrones básicos: contadores, acumuladores y centinelas.
| Patrón | Para qué sirve | Ejemplo |
|---|---|---|
| Contador | Cuenta cuántas veces ocurre algo | contador++ |
| Acumulador | Suma o acumula valores | suma += nota |
| Centinela | Valor especial que indica cuándo terminar | -1 para salir |
Ejemplo completo:
Scanner sc = new Scanner(System.in);
int contador = 0;
double suma = 0;
double nota;
System.out.print("Introduce una nota (-1 para terminar): ");
nota = sc.nextDouble();
while (nota != -1) {
suma += nota;
contador++;
System.out.print("Introduce otra nota (-1 para terminar): ");
nota = sc.nextDouble();
}
if (contador > 0) {
double media = suma / contador;
System.out.println("Media: " + media);
} else {
System.out.println("No se han introducido notas.");
}
sc.close();
El documento presenta estos tres patrones como bases importantes para trabajar con bucles: contador para contar, acumulador para sumar y centinela como valor especial de parada.
Este tipo de ejemplo es muy potente porque combina varias estructuras de control en Java: un while, un if else, un contador, un acumulador y una condición de salida.
Bucles anidados y bucles infinitos
Un bucle anidado es un bucle dentro de otro. Se usa para recorrer tablas, matrices o combinaciones de valores.
for (int fila = 1; fila <= 3; fila++) {
for (int columna = 1; columna <= 4; columna++) {
System.out.print("[" + fila + "," + columna + "] ");
}
System.out.println();
}
Por cada vuelta del bucle externo, el bucle interno se ejecuta completo. Este patrón aparece mucho cuando se trabajan estructuras de datos bidimensionales.
El problema contrario es el bucle infinito. Ocurre cuando la condición nunca deja de cumplirse.
int contador = 1;
while (contador <= 5) {
System.out.println(contador);
// Falta contador++
}
Aquí contador siempre vale 1, así que la condición contador <= 5 siempre será verdadera. El programa no termina.
Cuando reviso código de principiantes, una de las primeras cosas que miro en un while es si dentro del bucle hay algo que pueda hacer falsa la condición. Si no lo hay, probablemente tenemos un bucle infinito esperando a aparecer.
Sentencias de salto: break, continue y return
Las sentencias de salto también forman parte de las estructuras de control en Java. Sirven para alterar el flujo normal de ejecución.
Las más importantes son:
breakcontinuereturn
Son útiles, pero conviene usarlas con cabeza. Si mezclas demasiados saltos en la misma zona del programa, el código puede volverse difícil de seguir.
Cuándo usar break
La sentencia break finaliza el switch o bucle más cercano.
for (int i = 1; i <= 10; i++) {
if (i == 5) {
break;
}
System.out.println(i);
}
System.out.println("Fin del bucle");
Este código imprime 1, 2, 3 y 4. Cuando i vale 5, se ejecuta break y el bucle termina.
En un switch, break sirve para evitar que se ejecuten los siguientes case.
switch (opcion) {
case 1:
System.out.println("Alta");
break;
case 2:
System.out.println("Baja");
break;
default:
System.out.println("Opción no válida");
}
Cuándo usar continue
La sentencia continue no termina el bucle completo. Solo salta a la siguiente vuelta.
for (int i = 1; i <= 5; i++) {
if (i == 3) {
continue;
}
System.out.println(i);
}
Este código imprime 1, 2, 4 y 5. Cuando i vale 3, se salta esa vuelta.
continue puede ser útil para ignorar valores que no interesan. Por ejemplo, saltar números negativos:
for (int numero = -2; numero <= 3; numero++) {
if (numero < 0) {
continue;
}
System.out.println(numero);
}
Cuándo usar return
La sentencia return termina la ejecución de un método. Si el método devuelve un valor, return debe devolverlo.
public static boolean esMayorDeEdad(int edad) {
return edad >= 18;
}
También puede usarse en métodos void para salir antes:
public static void mostrarMensaje(String mensaje) {
if (mensaje == null) {
return;
}
System.out.println(mensaje);
}
En programación real, return puede ayudar a simplificar métodos. En lugar de anidar muchos if, a veces es más claro salir antes cuando una condición no tiene sentido.
public static void procesarNota(double nota) {
if (nota < 0 || nota > 10) {
System.out.println("Nota no válida");
return;
}
System.out.println("Nota procesada");
}
Cómo evitar que los saltos vuelvan el código difícil de seguir
break, continue y return son herramientas útiles, pero no deberían convertir el código en un laberinto.
Una buena regla práctica:
- usa
breakpara salir claramente de unswitcho de un bucle; - usa
continuesolo cuando saltar una vuelta haga el código más claro; - usa
returnpara expresar salidas naturales de un método; - evita mezclar muchos saltos en bloques largos.
El documento también recomienda evitar mezclar demasiados break, continue y return si eso dificulta seguir el flujo del programa.
Control de excepciones en Java
El control de excepciones es otra parte fundamental de las estructuras de control en Java. Una excepción representa una situación anómala durante la ejecución del programa.
Puede aparecer, por ejemplo, cuando:
- el usuario escribe texto donde se esperaba un número;
- se intenta dividir entre cero;
- se accede a una posición inexistente de un array;
- se intenta usar una referencia
null; - se convierte un texto inválido en número.
Sin control de excepciones, un programa puede cerrarse bruscamente. Con try catch, podemos capturar el problema y mostrar un mensaje más claro al usuario.
Diferencia entre error de compilación, error de ejecución y error lógico
Antes de usar excepciones, conviene distinguir tres tipos de errores.
| Tipo de error | Cuándo ocurre | Ejemplo |
|---|---|---|
| Error de compilación | Antes de ejecutar | Falta un punto y coma |
| Error de ejecución | Mientras funciona el programa | División entre cero |
| Error lógico | El programa ejecuta, pero calcula mal | Media mal calculada |
Esta distinción es muy importante. El compilador no detecta todos los problemas. Un programa puede compilar perfectamente y aun así funcionar mal. Por eso son necesarias las pruebas y la depuración, tal como se remarca en el documento.
En mi caso, suelo decirlo así: que el programa “arranque” no significa que esté bien. Solo significa que Java ha podido ejecutarlo. Ahora hay que comprobar si hace lo que debe hacer.
Cómo funciona try catch
La estructura try catch permite intentar ejecutar código que podría fallar y capturar una excepción si se produce.
try {
int resultado = 10 / 0;
System.out.println(resultado);
} catch (ArithmeticException e) {
System.out.println("No se puede dividir entre cero.");
}
El bloque try contiene el código peligroso. El bloque catch captura la excepción.
Otro ejemplo habitual:
try {
int numero = Integer.parseInt("hola");
System.out.println(numero);
} catch (NumberFormatException e) {
System.out.println("El texto no se puede convertir a número.");
}
Dentro de las estructuras de control en Java, try catch no sirve para tomar decisiones normales, sino para controlar situaciones anómalas. No deberíamos usar excepciones para todo.
Capturar errores de entrada con Scanner
Un caso muy común en programas de consola es pedir un número y que el usuario escriba texto.
import java.util.InputMismatchException;
import java.util.Scanner;
public class LecturaSegura {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
try {
System.out.print("Introduce tu edad: ");
int edad = sc.nextInt();
System.out.println("Edad introducida: " + edad);
} catch (InputMismatchException e) {
System.out.println("Debes introducir un número entero.");
}
sc.close();
}
}
El documento utiliza este mismo tipo de situación para explicar cómo Scanner puede lanzar una InputMismatchException cuando se espera un número y se introduce otro tipo de dato.
Este ejemplo es muy útil porque enseña algo importante: un buen programa no debería mostrar al usuario un mensaje técnico incomprensible. Si alguien escribe mal un dato, conviene explicar el problema con claridad.
Para qué sirve finally
El bloque finally se ejecuta siempre, haya o no haya excepción. Suele usarse para cerrar recursos o hacer tareas de limpieza.
Scanner sc = new Scanner(System.in);
try {
System.out.print("Introduce un número: ");
int numero = sc.nextInt();
System.out.println("Número: " + numero);
} catch (InputMismatchException e) {
System.out.println("Entrada no válida.");
} finally {
sc.close();
System.out.println("Recurso cerrado.");
}
En programas sencillos puede no parecer tan importante, pero es bueno conocerlo desde el principio. Cerrar recursos es parte de escribir código responsable.
Validar antes o capturar una excepción: cuándo elegir cada opción
No todo debe resolverse con excepciones. Muchas veces es mejor validar antes.
Por ejemplo, para evitar una división entre cero:
int divisor = 0;
if (divisor != 0) {
int resultado = 10 / divisor;
System.out.println(resultado);
} else {
System.out.println("El divisor no puede ser cero.");
}
Aquí no hace falta provocar una excepción. Podemos comprobar antes si el divisor es válido.
Una buena guía:
| Situación | Mejor enfoque |
|---|---|
| Comprobar si una nota está entre 0 y 10 | Validación con if |
| Comprobar si una opción de menú existe | Validación con if o switch |
| Usuario escribe texto donde se esperaba un número | try catch o lectura como texto y validación |
| Archivo que puede no existir | Control de excepción |
Las estructuras de control en Java no compiten entre sí. Se combinan. Un programa sólido suele usar if, bucles, switch, try catch y pruebas.
Cómo crear un programa ejecutable combinando estructuras de control
Una vez entiendes las piezas por separado, llega lo importante: combinarlas en un programa completo. Ahí es donde las estructuras de control en Java empiezan a tener sentido de verdad.
Un programa de consola suele incluir:
- importaciones;
- clase principal;
- método
main; - variables iniciales;
- bucle principal;
- selección de opciones;
- control de errores;
- cierre de recursos.
El documento propone precisamente esta estructura para programas de consola con estructuras de control, incluyendo importaciones, clase principal, método main, variables, bucle principal, selección de opciones, validaciones, try catch y cierre de recursos.
La estructura básica con main
En Java, el método main es el punto de entrada de una aplicación de consola.
public class ProgramaPrincipal {
public static void main(String[] args) {
System.out.println("Programa iniciado");
}
}
A partir de ahí podemos añadir variables, menús, bucles, decisiones y control de errores.
Menú de consola con do while, switch, if y try catch
Un menú sencillo permite combinar varias estructuras de control en Java:
import java.util.InputMismatchException;
import java.util.Scanner;
public class MenuBasico {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int opcion = -1;
do {
System.out.println("1. Saludar");
System.out.println("2. Despedir");
System.out.println("0. Salir");
System.out.print("Opción: ");
try {
opcion = sc.nextInt();
switch (opcion) {
case 1:
System.out.println("Hola");
break;
case 2:
System.out.println("Adiós");
break;
case 0:
System.out.println("Fin del programa");
break;
default:
System.out.println("Opción no válida");
}
} catch (InputMismatchException e) {
System.out.println("Debes introducir un número.");
sc.nextLine();
}
} while (opcion != 0);
sc.close();
}
}
Aquí tenemos:
do whilepara repetir el menú;switchpara elegir la acción;try catchpara controlar una entrada incorrecta;breakpara cerrar cadacase;- una condición de salida con
opcion != 0.
Este ejemplo resume muy bien por qué las estructuras de control en Java son tan importantes. No estamos escribiendo instrucciones aisladas. Estamos diseñando comportamiento.
Caso práctico: gestión de notas paso a paso
Un caso muy completo es un programa de gestión de notas. El objetivo es introducir notas, calcular la media, contar aprobados y suspensos, controlar entradas incorrectas y repetir el menú hasta salir.
La lógica sería:
Inicio
Inicializar suma, contador, aprobados y suspensos
Repetir
Mostrar menú
Leer opción
Según opción:
1 -> Pedir nota, validar, acumular y contar
2 -> Mostrar resumen
0 -> Salir
otra -> Mostrar error
Mientras opción no sea 0
Fin
Este diseño coincide con el caso práctico del documento, que combina do while, switch, if else, try catch, contadores y acumuladores para gestionar notas.
Una versión simplificada podría ser:
import java.util.InputMismatchException;
import java.util.Scanner;
public class GestionNotas {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double suma = 0;
int contador = 0;
int aprobados = 0;
int suspensos = 0;
int opcion = -1;
do {
System.out.println("\n--- MENÚ DE NOTAS ---");
System.out.println("1. Introducir nota");
System.out.println("2. Mostrar resumen");
System.out.println("0. Salir");
System.out.print("Elige una opción: ");
try {
opcion = sc.nextInt();
switch (opcion) {
case 1:
System.out.print("Introduce una nota entre 0 y 10: ");
double nota = sc.nextDouble();
if (nota < 0 || nota > 10) {
System.out.println("La nota debe estar entre 0 y 10.");
} else {
suma += nota;
contador++;
if (nota >= 5) {
aprobados++;
} else {
suspensos++;
}
System.out.println("Nota registrada correctamente.");
}
break;
case 2:
if (contador == 0) {
System.out.println("Todavía no hay notas registradas.");
} else {
double media = suma / contador;
System.out.println("Notas registradas: " + contador);
System.out.println("Media: " + media);
System.out.println("Aprobados: " + aprobados);
System.out.println("Suspensos: " + suspensos);
}
break;
case 0:
System.out.println("Saliendo del programa...");
break;
default:
System.out.println("Opción no válida.");
}
} catch (InputMismatchException e) {
System.out.println("Entrada no válida. Debes introducir un número.");
sc.nextLine();
}
} while (opcion != 0);
sc.close();
}
}
Este ejemplo es muy completo para practicar estructuras de control en Java porque obliga a tomar decisiones, repetir un menú, validar rangos, acumular datos, contar resultados y controlar errores.
Depuración: cómo encontrar errores sin tocar código al azar
Depurar es localizar, analizar y corregir errores. No consiste en cambiar líneas al azar hasta que algo parezca funcionar.
Esta parte me parece especialmente importante. En desarrollo real, y también en clase, depurar bien ahorra muchísimo tiempo. Cuando un alumno se acostumbra a observar variables, comprobar condiciones y seguir el flujo paso a paso, deja de depender de la intuición y empieza a razonar como programador.
Qué tipos de errores puedes depurar
Podemos depurar varios tipos de errores:
| Tipo | Síntoma |
|---|---|
| Sintaxis | El programa no compila |
| Ejecución | El programa se detiene o lanza una excepción |
| Lógico | El programa termina, pero el resultado es incorrecto |
| Flujo | Entra en un if que no debería |
| Bucle | Repite demasiado, demasiado poco o nunca termina |
El documento define la depuración como un proceso para localizar, analizar y corregir errores, comparando el comportamiento real del programa con el esperado. También destaca que depurar ayuda a entender cómo se ejecuta realmente el código.
Puntos de ruptura y ejecución paso a paso
Un punto de ruptura, o breakpoint, es una marca que colocamos en una línea para que el depurador detenga ahí la ejecución.
int suma = 0;
for (int i = 1; i <= 5; i++) {
suma += i; // Coloca aquí un punto de ruptura
}
System.out.println("Suma: " + suma);
Al detener el programa, podemos ver cuánto vale i, cuánto vale suma y cómo cambian en cada vuelta.
Las acciones típicas del depurador son:
| Acción | Para qué sirve |
|---|---|
| Step Over | Ejecutar la línea actual sin entrar en métodos |
| Step Into | Entrar dentro del método llamado |
| Step Return | Salir del método actual |
| Resume | Continuar hasta el siguiente punto de ruptura |
| Terminate | Finalizar la ejecución |
Cuando se aprenden estructuras de control en Java, depurar es una herramienta fantástica porque permite ver el flujo en directo. Puedes comprobar si un if entra donde esperabas, si un while repite demasiadas veces o si un contador aumenta correctamente.
Inspección de variables
Durante la depuración, el IDE muestra el valor de las variables.
Conviene revisar:
| Variable | Qué comprobar |
|---|---|
contador | Si aumenta correctamente |
suma | Si acumula los valores esperados |
opcion | Si recoge la opción real del usuario |
nota | Si está dentro del rango permitido |
mensaje | Si contiene el texto correcto |
En el caso del programa de notas, yo pondría puntos de ruptura después de leer la opción y después de leer la nota. Así se puede comprobar qué camino sigue el switch, si el if valida correctamente y si los contadores cambian como deben.
Método ordenado para depurar un programa
Un método sencillo para depurar sería:
- Reproduce el error.
- Anota qué entrada provoca el fallo.
- Lee el mensaje completo si aparece una excepción.
- Localiza la zona probable del problema.
- Coloca un punto de ruptura antes.
- Ejecuta paso a paso.
- Observa variables y condiciones.
- Corrige una cosa cada vez.
- Vuelve a probar.
Esta forma de trabajar conecta muy bien con mi manera de entender la programación: estructura, claridad y resultados reales. No se trata de tocar por tocar. Se trata de observar qué hace el programa y compararlo con lo que debería hacer.
Comentarios, documentación y pruebas
Las estructuras de control en Java no solo deben funcionar. También deben entenderse, probarse y mantenerse.
Un programa puede compilar, ejecutar y aun así ser difícil de leer. Por eso los comentarios, la documentación y las pruebas forman parte de escribir buen código.
Comentarios útiles frente a comentarios que solo meten ruido
Un comentario útil explica una decisión, no repite lo evidente.
Buen comentario:
// Calcula el importe del descuento antes de restarlo al precio inicial
double importeDescuento = precio * porcentaje / 100;
Comentario poco útil:
// Suma uno a contador
contador++;
Si el código ya se entiende, no hace falta comentarlo todo. En proyectos reales, demasiados comentarios obvios pueden hacer más ruido que ayuda.
Javadoc y documentación externa básica
Java permite varios tipos de comentarios:
// Comentario de una línea
/*
Comentario de bloque
*/
/**
* Calcula la media de dos notas.
* @param nota1 primera nota
* @param nota2 segunda nota
* @return media aritmética
*/
public static double calcularMedia(double nota1, double nota2) {
return (nota1 + nota2) / 2;
}
La documentación externa puede incluir:
- objetivo del programa;
- datos de entrada;
- proceso que realiza;
- datos de salida;
- estructuras de control utilizadas;
- excepciones o validaciones aplicadas;
- pruebas realizadas;
- problemas encontrados y soluciones.
El documento indica que una práctica sencilla debería documentar el objetivo, entradas, proceso, salidas, estructuras utilizadas, validaciones, pruebas, evidencias y problemas encontrados.
Pruebas normales, límite y erróneas
Probar es tan importante como programar.
En el programa de notas, habría que probar:
| Prueba | Entrada | Resultado esperado |
|---|---|---|
| Opción válida | 1 y nota 7 | Nota registrada como aprobada |
| Nota límite inferior | 0 | Nota registrada como suspensa |
| Nota límite superior | 10 | Nota registrada como aprobada |
| Nota fuera de rango | 12 | Mensaje de error |
| Resumen sin notas | 2 al inicio | Mensaje indicando que no hay notas |
| Entrada no numérica | texto | Mensaje de entrada no válida |
| Salir | 0 | El programa termina |
El documento propone pruebas de este tipo para el caso de gestión de notas y recomienda revisar también puntos de depuración concretos, como la opción del menú, la nota introducida y los contadores.
Errores frecuentes al aprender estructuras de control en Java
Al aprender estructuras de control en Java, hay errores que se repiten muchísimo. Conocerlos ayuda a detectarlos antes.
Confundir = con ==
int opcion = 1; // asignación
opcion == 1 // comparación
Usa = para asignar y == para comparar valores primitivos.
Olvidar break en un switch
switch (opcion) {
case 1:
System.out.println("Uno");
case 2:
System.out.println("Dos");
}
Si falta break, se pueden ejecutar varios case seguidos.
Crear un bucle infinito sin querer
int contador = 1;
while (contador <= 5) {
System.out.println(contador);
}
Falta actualizar contador.
No limpiar Scanner tras una entrada incorrecta
Cuando Scanner lanza una InputMismatchException, muchas veces conviene limpiar la entrada:
sc.nextLine();
Si no lo haces, el programa puede repetir el catch continuamente.
No probar casos límite
Si un programa acepta notas entre 0 y 10, no basta con probar 7. Hay que probar 0, 10, negativos, valores mayores que 10 y entradas no numéricas.
El documento recoge estos errores frecuentes: usar =, comparar String con ==, olvidar actualizar contadores, olvidar break, no limpiar Scanner, no probar casos límite y escribir comentarios inútiles.
Ejercicios recomendados para practicar
La mejor forma de dominar estructuras de control en Java es escribir programas pequeños. No hace falta empezar con proyectos enormes. De hecho, suele ser mejor trabajar ejercicios muy concretos.
Clasificador de edad
Objetivo: pedir una edad y mostrar si la persona es menor de edad, adulta o jubilada.
Scanner sc = new Scanner(System.in);
System.out.print("Introduce tu edad: ");
int edad = sc.nextInt();
if (edad < 0) {
System.out.println("Edad no válida");
} else if (edad < 18) {
System.out.println("Menor de edad");
} else if (edad < 65) {
System.out.println("Adulto");
} else {
System.out.println("Jubilado");
}
sc.close();
Tabla de multiplicar
Objetivo: practicar el bucle for.
Scanner sc = new Scanner(System.in);
System.out.print("Introduce un número: ");
int numero = sc.nextInt();
for (int i = 1; i <= 10; i++) {
System.out.println(numero + " x " + i + " = " + (numero * i));
}
sc.close();
Menú repetitivo
Objetivo: combinar do while y switch.
Scanner sc = new Scanner(System.in);
int opcion;
do {
System.out.println("1. Saludar");
System.out.println("2. Despedir");
System.out.println("0. Salir");
System.out.print("Opción: ");
opcion = sc.nextInt();
switch (opcion) {
case 1:
System.out.println("Hola");
break;
case 2:
System.out.println("Adiós");
break;
case 0:
System.out.println("Fin");
break;
default:
System.out.println("Opción no válida");
}
} while (opcion != 0);
sc.close();
Entrada segura con try catch
Objetivo: evitar que el programa se cierre si el usuario escribe texto donde se espera un número.
try {
System.out.print("Introduce un número: ");
int numero = sc.nextInt();
System.out.println("Número introducido: " + numero);
} catch (InputMismatchException e) {
System.out.println("Error: debes introducir un número entero.");
sc.nextLine();
}
Estos ejercicios aparecen alineados con las actividades guiadas del documento: clasificador de edad, tabla de multiplicar, menú repetitivo y entrada segura.
Resumen final
Las estructuras de control en Java permiten que un programa deje de ser una simple secuencia lineal de instrucciones. Gracias a ellas, el código puede tomar decisiones, repetir procesos, interrumpir flujos, controlar errores y facilitar la depuración.
Las estructuras de selección, como if, if else, else if y switch, permiten elegir entre distintos caminos. Las estructuras de repetición, como while, do while y for, permiten ejecutar instrucciones varias veces. Las sentencias de salto, como break, continue y return, modifican el recorrido normal del programa. Y el control de excepciones con try catch ayuda a gestionar situaciones anómalas sin que el programa se cierre de forma brusca.
Pero dominar estructuras de control en Java no consiste solo en conocer la sintaxis. Lo importante es saber cuándo usar cada una. En un menú, probablemente usarás do while y switch. Para validar una nota, un if puede ser suficiente. Para repetir una acción un número concreto de veces, un for suele ser lo más claro. Para controlar una entrada incorrecta, try catch puede salvar el programa.
En mi experiencia, tanto desarrollando como enseñando, la diferencia se nota cuando el código empieza a tener intención. Un buen programa no solo funciona: se entiende, se prueba, se depura y se puede mantener. Y las estructuras de control en Java son la base para conseguirlo.
Preguntas frecuentes sobre estructuras de control en Java
¿Qué son las estructuras de control en Java?
Las estructuras de control en Java son instrucciones que permiten modificar el flujo de ejecución de un programa. Sirven para tomar decisiones, repetir bloques de código, interrumpir procesos, controlar errores y depurar programas.
¿Qué diferencia hay entre if y switch?
if se usa para evaluar condiciones generales. switch se usa cuando queremos comparar una misma variable con varios valores concretos, como opciones de menú.
¿Qué diferencia hay entre while, do while y for?
while comprueba la condición antes de ejecutar el bloque. do while ejecuta el bloque al menos una vez y comprueba la condición al final. for suele usarse cuando conocemos el número de repeticiones.
¿Para qué sirve try catch en Java?
try catch sirve para capturar excepciones durante la ejecución del programa. Permite controlar errores como entradas incorrectas, divisiones entre cero o conversiones inválidas, mostrando mensajes comprensibles en lugar de cerrar el programa bruscamente.
¿Qué es un bucle infinito?
Un bucle infinito ocurre cuando la condición de repetición nunca deja de cumplirse. Suele pasar cuando olvidamos actualizar la variable de control dentro del bucle.
¿Qué es un punto de ruptura?
Un punto de ruptura, o breakpoint, es una marca colocada en una línea de código para que el depurador detenga ahí la ejecución. Sirve para observar variables y comprobar qué camino sigue el programa.
¿Por qué es importante depurar al aprender estructuras de control en Java?
Porque permite ver cómo se ejecuta realmente el código. Al depurar, puedes comprobar si una condición se cumple, si un bucle repite correctamente o si una variable tiene el valor esperado.


