El switch de Java ha cambiado tanto en los últimos años que el que aparece en los tutoriales viejos y el que se escribe hoy casi no se parecen. Esta lección va de eso, de los bucles, y de los arrays: por qué no crecen y por qué == no sirve para compararlos.
Lo de siempre, en dos líneas
if, else, while y for se escriben como en C, Java fue quien los copió primero. Lo que merece la pena mirar es el for de recorrido y el operador ternario:
for (String fr : frutas) IO.println(" for-each: " + fr);
int edad = 17;
IO.println((edad >= 18 ? "mayor" : "menor") + " de edad");
salidafor-each: pera for-each: uva for-each: kiwi menor de edad
Y un detalle que evita muchos errores: && y || cortocircuitan. Si el primer operando ya decide el resultado, el segundo no llega a evaluarse:
String texto = null;
if (texto != null && texto.length() > 0) { ... } // no revienta
Por eso este orden es idiomático y el contrario explota.
El switch de hoy no se parece al de ayer
El switch clásico es de las cosas más feas del lenguaje: hay que acordarse del break en cada rama o la ejecución se cae a la siguiente. Compáralos:
// el de toda la vida
String viejo;
switch (dia) {
case 6:
case 7:
viejo = "fin de semana";
break;
default:
viejo = "laborable";
}
// el moderno: es una expresión, devuelve un valor
String nuevo = switch (dia) {
case 6, 7 -> "fin de semana";
default -> "laborable";
};
salidadía 5: viejo=laborable nuevo=laborable día 6: viejo=fin de semana nuevo=fin de semana día 7: viejo=fin de semana nuevo=fin de semana
La versión con flecha no se cae a la rama siguiente, agrupa casos con comas y devuelve un valor, así que se puede asignar directamente. Si una rama necesita varias líneas, se abre una llave y se devuelve con yield:
int dias = switch (mes) {
case 1, 3, 5, 7, 8, 10, 12 -> 31;
case 4, 6, 9, 11 -> 30;
case 2 -> {
int anio = 2026;
boolean bisiesto = (anio % 4 == 0 && anio % 100 != 0) || anio % 400 == 0;
yield bisiesto ? 29 : 28;
}
default -> throw new IllegalArgumentException("mes inválido: " + mes);
};
salidafebrero de 2026 tiene 28 días
switch es una expresión, el compilador exige que cubras todos los casos posibles. Si trabajas con un enum y añades un valor nuevo, todos los switch que lo usen dejarán de compilar hasta que los actualices. Es de las mejores redes de seguridad del lenguaje, y se pierde en cuanto escribes un default que no hace nada.Y ahora también mira el tipo
String q = switch (o) {
case null -> "nada";
case Integer i when i > 10 -> "un entero grande: " + i;
case Integer i -> "un entero: " + i;
case String s -> "un texto de " + s.length() + " letras";
case List<?> l -> "una lista de " + l.size();
default -> "otra cosa: " + o;
};
salidaun entero grande: 42 un texto de 4 letras otra cosa: 3.5 una lista de 2 nada
Fíjate en lo que hace cada rama: comprueba el tipo y declara la variable ya convertida, sin cast. El when añade una condición extra, y case null permite tratar el nulo como un caso más en vez de reventar antes de empezar. Esto llegó en Java 21 y cambia bastante cómo se escribe el código que maneja varios tipos.
Arrays
int[] a = new int[4]; // se rellena con ceros
String[] b = new String[3]; // se rellena con null
double[] c = {1.5, 2.5, 3.5};
salidaint[4] = [0, 0, 0, 0] String[3] = [null, null, null] literal = [1.5, 2.5, 3.5] longitud 3
Lo primero que hay que interiorizar: un array no cambia de tamaño. Lo que se declara es lo que hay. Si necesitas que crezca, lo que quieres es una List, y eso es la lección 7.
Lo segundo: se pregunta con length, sin paréntesis. En las cadenas es length() y en las listas size(). No hay lógica detrás, hay que recordarlo.
frutas[3] con tres frutasjava.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
La clase Arrays, que resuelve casi todo
int[] n = {5, 3, 9, 1};
Arrays.sort(n);
IO.println(" ordenado " + Arrays.toString(n));
IO.println(" binarySearch(9) -> " + Arrays.binarySearch(n, 9));
IO.println(" copyOf(2) " + Arrays.toString(Arrays.copyOf(n, 2)));
IO.println(" equals " + Arrays.equals(new int[]{1,2}, new int[]{1,2}));
IO.println(" con == ? " + (new int[]{1,2} == new int[]{1,2}));
salidaordenado [1, 3, 5, 9] binarySearch(9) -> 3 copyOf(2) [1, 3] equals true con == ? false
equals pero no son ==, porque == compara referencias: pregunta «¿son el mismo objeto?», no «¿valen lo mismo?». Lo mismo pasa con String, y es el error número uno de quien empieza. Para comparar contenido: equals en objetos, Arrays.equals en arrays.Y para imprimir: Arrays.toString. Si imprimes un array a secas sale algo como [I@1b6d3586, que es el tipo y la dirección. Para arrays de arrays, Arrays.deepToString.
Dos dimensiones
int[][] m = {{1,2,3},{4,5,6}};
for (int[] fila : m) IO.println(" " + Arrays.toString(fila));
IO.println(" m[1][2] = " + m[1][2] + " filas " + m.length + " columnas " + m[0].length);
salida[1, 2, 3] [4, 5, 6] m[1][2] = 6 filas 2 columnas 3 deepToString: [[1, 2, 3], [4, 5, 6]]
En Java una matriz es un array de arrays, no una tabla. Eso significa que cada fila podría tener una longitud distinta, y que m[0].length es la longitud de la primera fila, no «el número de columnas» de nada.
Salir de un bucle
for (int i = 1; i <= 5; i++) {
if (i == 3) continue; // salta esta vuelta
if (i == 5) break; // abandona el bucle
IO.print(i + " ");
}
buscar:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i * j == 2) { IO.println("encontrado en i=" + i + " j=" + j); break buscar; }
}
}
salida1 2 4 encontrado en i=1 j=2
Lo de la etiqueta —buscar: y break buscar;— es lo más parecido a un goto que tiene Java, y existe por una razón concreta: salir de dos bucles anidados de golpe. Un break normal solo abandona el de dentro. Se usa poco, pero cuando hace falta, la alternativa es una variable de bandera que se lee peor.
Lo esencial
| Para… | Lo que se usa hoy |
|---|---|
| Elegir entre varios valores | switch con ->, como expresión |
| Una rama de varias líneas | llaves y yield |
| Distinguir por tipo | case Tipo x ->, con when si hace falta |
| Recorrer | for (T x : coleccion) |
| Tamaño | array.length, cadena.length(), lista.size() |
| Comparar contenido | equals / Arrays.equals, nunca == |
| Imprimir un array | Arrays.toString / deepToString |
| Salir de dos bucles | una etiqueta y break etiqueta; |
Todo el código de esta lección se compiló y se ejecutó con Java 25 LTS en un contenedor limpio antes de publicarla; las salidas y los errores del compilador están copiados de esa ejecución.