Strings en Java — ejercicios con palíndromos, palabras y cifrado César (y una trampa con == que no vi a tiempo)

En un ejercicio me pidieron comprobar si dos palabras eran anagramas. Ordené las letras de las dos, comparé los textos con == y el programa me daba false aunque tenían exactamente las mismas letras. No vi por qué hasta mucho después. Era el mismo == de siempre, el que compara direcciones y no contenido, escondido detrás de un código que parecía perfecto. Esa trampa sale otra vez en este artículo, en la sección final.

Aquí tienes tres ejercicios de Strings resueltos y comentados (palíndromos, contar palabras y vocales, y cifrado César) y, al final, una sección de «¿Qué falla aquí?» con tres fragmentos con error para cazar. Si todavía no has visto la teoría o la práctica, conviene pasar antes por ellas.

Cómo sacarle partido: lee el enunciado, intenta resolverlo tú y solo entonces mira la solución. Aunque no te salga, el intento hace que la solución se entienda diez veces mejor. Y cuando termines, prueba con casos raros: texto vacío, solo espacios, mayúsculas, tildes.

Ejercicio 1: palíndromos

Enunciado. Escribe esPalindromo(String texto), que devuelve true si el texto se lee igual de izquierda a derecha que de derecha a izquierda. Tiene que ignorar mayúsculas, espacios, signos de puntuación y tildes.

Ejemplos. "ana", "Ana" y "Anita lava la tina" son palíndromos; "hola" no lo es. Una frase como "¿Acaso hubo búhos acá?" también lo es, una vez limpia.

Divide el problema en dos: primero limpiar el texto (minúsculas, sin tildes, solo letras y dígitos) y después comprobar si lo limpio es capicúa. Para comprobar no hace falta darle la vuelta al texto: basta con comparar el primer carácter con el último, el segundo con el penúltimo, y así hasta encontrarte en el medio.

Solución comentada

// Pasa a minúsculas y quita tildes y eñes
static String normalizar(String s) {
    String r = s.trim().toLowerCase();
    r = r.replace('á', 'a');
    r = r.replace('é', 'e');
    r = r.replace('í', 'i');
    r = r.replace('ó', 'o');
    r = r.replace('ú', 'u');
    r = r.replace('ü', 'u');
    r = r.replace('ñ', 'n');
    return r;
}

// Se queda solo con las letras y los dígitos (ya normalizados)
static String soloLetrasYDigitos(String s) {
    String r = "";
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (Character.isLetterOrDigit(c)) {
            r += c;
        }
    }
    return r;
}

static boolean esPalindromo(String texto) {
    String limpio = soloLetrasYDigitos(normalizar(texto));
    int i = 0;                                   // desde el principio
    int j = limpio.length() - 1;                 // desde el final
    while (i < j) {
        if (limpio.charAt(i) != limpio.charAt(j)) {   // char: aquí sí se puede usar !=
            return false;
        }
        i++;
        j--;
    }
    return true;                                 // si nunca hubo diferencia (o está vacío), es palíndromo
}

Por qué funciona

normalizar es el método de la práctica: minúsculas primero y después quitar tildes. soloLetrasYDigitos recorre el texto con charAt y se queda únicamente con los caracteres para los que Character.isLetterOrDigit devuelve true; así desaparecen espacios, comas, interrogaciones y todo lo que no sea una letra o un número.

La comprobación usa dos posiciones que avanzan una hacia la otra: i desde el principio y j desde el final. Si en algún momento charAt(i) y charAt(j) son distintos, ya no es palíndromo. Si el bucle termina sin encontrar diferencias, sí lo es. Fíjate en que aquí sí se puede usar !=: estamos comparando char, que es un tipo primitivo, no String.

Casos de prueba

String[] pruebas = {"ana", "Ana", "Anita lava la tina", "Dábale arroz a la zorra el abad",
                    "¿Acaso hubo búhos acá?", "hola", "a", ""};
for (String p : pruebas) {
    System.out.println("\"" + p + "\" -> " + esPalindromo(p));
}
"ana" -> true
"Ana" -> true
"Anita lava la tina" -> true
"Dábale arroz a la zorra el abad" -> true
"¿Acaso hubo búhos acá?" -> true
"hola" -> false
"a" -> true
"" -> true

Preguntas que te puedes hacer

¿Por qué while (i < j) y no i <= j? Cuando i y j se encuentran en el mismo carácter (texto de longitud impar), ese carácter central se compara consigo mismo y siempre es igual, así que no hace falta mirarlo. Y cuando se cruzan (i > j), ya se ha comprobado todo.

¿Por qué un texto vacío o de una sola letra da true? Porque no hay ningún par de caracteres que pueda ser distinto. Al principio j vale -1 o 0, el bucle no se ejecuta ni una vez y se llega directamente al return true. Es un caso límite que conviene probar siempre.

¿Por qué no darle la vuelta al texto con StringBuilder.reverse() y comparar con equals? También funciona (limpio.equals(new StringBuilder(limpio).reverse().toString())) y es más corto. La versión con dos posiciones evita crear un texto nuevo y es la que se pide en muchos exámenes, porque enseña a recorrer desde los dos extremos.

¿Qué hace Character.isLetterOrDigit? Es un método de la clase Character que dice si un char es una letra o un dígito. Funciona también con letras acentuadas ('é' da true), pero como las hemos normalizado antes, ya nos llegan sin tilde. Con '¿' o ' ' da false.

Comparar el texto con su reverso usando ==. Aunque el contenido sea idéntico, == compara direcciones y dará false casi siempre. Es justo el fallo del anagrama de mi anécdota. Con textos: equals; con caracteres sueltos: == o !=.

Si olvidas normalizar (minúsculas y tildes), "Ana" falla ('A' y 'a' son caracteres distintos) y "Dábale arroz a la zorra el abad" también. Es lo que me pasó a mí con el primer comprobador: funcionaba con "ana" y nada más.

Ejercicio 2: contar palabras y vocales

Enunciado. Escribe tres métodos:

  • contarPalabras(String texto): devuelve cuántas palabras tiene el texto. Las palabras pueden estar separadas por uno o varios espacios, y puede haber espacios al principio y al final.
  • palabraMasLarga(String texto): devuelve la palabra más larga (si hay empate, la primera).
  • contarVocales(String palabra): devuelve cuántas vocales tiene una palabra, contando también las acentuadas.

Ejemplo. Con " El gato come pescado fresco ": 5 palabras, la más larga es pescado y tiene 3 vocales.

split(" ") no sirve para separar palabras si hay espacios dobles: deja Strings vacías en medio. Para esto existe el separador "\\s+" (uno o más espacios) que viste en la teoría. Pero ojo con los extremos del texto y con el texto vacío.

Solución comentada

static int contarPalabras(String texto) {
    String limpio = texto.trim();
    if (limpio.isEmpty()) {
        return 0;                                // "".split("\\s+") daría 1 elemento (una String vacía)
    }
    return limpio.split("\\s+").length;
}

static int contarVocales(String palabra) {
    String p = normalizar(palabra);              // así 'ó' cuenta como 'o'
    int vocales = 0;
    for (int i = 0; i < p.length(); i++) {
        if ("aeiou".indexOf(p.charAt(i)) != -1) {
            vocales++;
        }
    }
    return vocales;
}

static String palabraMasLarga(String texto) {
    String limpio = texto.trim();
    if (limpio.isEmpty()) {
        return "";
    }
    String[] palabras = limpio.split("\\s+");
    String mejor = palabras[0];
    for (int i = 1; i < palabras.length; i++) {
        if (palabras[i].length() > mejor.length()) {   // > estricto: en empate gana la primera
            mejor = palabras[i];
        }
    }
    return mejor;
}

Por qué funciona

contarPalabras hace dos cosas antes de dividir. Primero, trim() quita los espacios de los extremos (si no, un espacio al principio dejaría una String vacía como primer elemento). Segundo, comprueba si lo que queda está vacío: "".split("\\s+") no devuelve un array vacío sino uno con un elemento (la String vacía), así que sin esa comprobación el texto vacío contaría como 1 palabra.

contarVocales normaliza primero para que ó cuente como o, y después recorre la palabra preguntando si cada carácter está dentro de "aeiou" con indexOf. Si indexOf devuelve algo distinto de -1, es una vocal.

palabraMasLarga es el patrón de «buscar el máximo» que viste con los arrays: parte de la primera palabra y reemplaza mejor solo cuando encuentra una estrictamente más larga, por eso en caso de empate gana la primera.

Casos de prueba

String frase = "  El   gato  come   pescado fresco  ";
System.out.println("Palabras: " + contarPalabras(frase));
System.out.println("Más larga: " + palabraMasLarga(frase));
for (String palabra : frase.trim().split("\\s+")) {
    System.out.println("  " + palabra + ": " + contarVocales(palabra) + " vocales");
}
System.out.println("Vacío: " + contarPalabras("") + ", solo espacios: " + contarPalabras("   "));
System.out.println("Canción: " + contarVocales("Canción") + " vocales");
Palabras: 5
Más larga: pescado
  El: 1 vocales
  gato: 2 vocales
  come: 2 vocales
  pescado: 3 vocales
  fresco: 2 vocales
Vacío: 0, solo espacios: 0
Canción: 3 vocales

Preguntas que te puedes hacer

¿Por qué hay que hacer trim() antes de split("\\s+")? Porque si el texto empieza por espacios, split devuelve una String vacía como primer elemento (" hola mundo".split("\\s+") da ["", "hola", "mundo"]). Con trim() antes, ese elemento vacío desaparece. Curiosamente, los espacios del final no dan problema, porque split descarta los vacíos del final, pero los del principio sí.

¿Y si el texto contiene signos de puntuación, como "Hola, mundo."? Con este código, "Hola," y "mundo." cuentan como palabras con la coma y el punto pegados, y palabraMasLarga los incluiría en la longitud. Para el enunciado, está bien; si quisieras limpiarlos, podrías pasar cada palabra por soloLetrasYDigitos del ejercicio anterior.

¿Por qué "aeiou".indexOf(...) en vez de cinco comparaciones? Es más corto y se amplía fácilmente: si quisieras contar también otros caracteres, basta con cambiar el texto. Es el truco que usamos en la teoría.

¿Qué pasa si hay una palabra con mayúsculas, como "El"? contarVocales la normaliza antes ("el"), así que la E cuenta como vocal. Sin normalizar, 'E' no estaría dentro de "aeiou" y la contaría mal.

Hacer texto.split("\\s+").length directamente sobre un texto vacío o con espacios al principio. En el primer caso devuelve 1 en vez de 0, y en el segundo cuenta una palabra fantasma. Siempre: trim() primero y comprobar si queda vacío.

Si un método necesita las palabras de un texto en varios sitios (contar, buscar la más larga…), sácalas en un método aparte que devuelva el array ya limpio. Aquí lo hemos repetido para que cada método se entienda por sí solo, pero en un programa más largo evitarías duplicar el trim y el split.

Ejercicio 3: cifrado César

Enunciado. El cifrado César sustituye cada letra por la que está n posiciones más adelante en el alfabeto, volviendo al principio al pasar la z. Escribe:

  • cifrar(String texto, int desplazamiento): cifra el texto. Conserva mayúsculas y minúsculas, y deja sin cambiar todo lo que no sea una letra de la a a la z (espacios, signos, números…).
  • descifrar(String texto, int desplazamiento): deshace el cifrado.

Ejemplo. cifrar("Hola, Mundo!", 3) da "Krod, Pxqgr!". Con "xyz" y desplazamiento 3 sale "abc".

Los char son números por dentro, así que se puede sumar y restar con ellos. La posición de una letra en el alfabeto es c - 'a' (0 para la a, 25 para la z). Súmale el desplazamiento, da la vuelta con % 26 y vuelve a sumar 'a' para obtener la letra. Y piensa qué pasa con desplazamientos negativos o mayores que 26.

Solución comentada

static String cifrar(String texto, int desplazamiento) {
    int d = ((desplazamiento % 26) + 26) % 26;   // lo deja entre 0 y 25, también con negativos
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < texto.length(); i++) {
        char c = texto.charAt(i);
        if (c >= 'a' && c <= 'z') {
            sb.append((char) ('a' + (c - 'a' + d) % 26));
        } else if (c >= 'A' && c <= 'Z') {
            sb.append((char) ('A' + (c - 'A' + d) % 26));
        } else {
            sb.append(c);                        // espacios, signos, tildes...: se quedan como están
        }
    }
    return sb.toString();
}

static String descifrar(String texto, int desplazamiento) {
    return cifrar(texto, -desplazamiento);       // descifrar es cifrar al revés
}

Por qué funciona

Tomemos la x con desplazamiento 3. Su posición es 'x' - 'a' = 23. Sumamos 3: 26. Con % 26 da 0, que vuelve al principio. Sumamos 'a' y obtenemos la a. Esa es la «vuelta» del alfabeto: el % 26 hace que después de la z venga la a.

La conversión (char) es necesaria: 'a' + algo es un int, y para añadirlo como letra al StringBuilder hay que volver a convertirlo en char. Y usamos StringBuilder porque vamos a añadir carácter a carácter: es el caso típico del que hablábamos en la teoría, donde crear un String nuevo en cada vuelta sería innecesario.

Una vez cifrado, descifrar es exactamente cifrar con el desplazamiento opuesto: si desplazaste 3 hacia delante, desplazas −3 (es decir, 23) para volver. Por eso descifrar no tiene código propio.

Casos de prueba

String mensaje = "Hola, Mundo!";
String cifrado = cifrar(mensaje, 3);
System.out.println(cifrado);
System.out.println(descifrar(cifrado, 3));
System.out.println(cifrar("xyz", 3));
System.out.println(cifrar("abc", -1));
System.out.println(cifrar("abc", 29) + " " + cifrar("abc", 3));
System.out.println(cifrar("Canción", 1));
Krod, Pxqgr!
Hola, Mundo!
abc
zab
def def
Dbodjóo

Fíjate en cuatro cosas: el mensaje descifrado vuelve a ser el original; "xyz" da la vuelta y termina en "abc"; con -1, la a pasa a ser z; y desplazar 29 da lo mismo que desplazar 3 (porque 29 % 26 = 3). Y en "Canción", la ó se queda como está: el programa solo toca las letras de la a a la z sin tilde.

Preguntas que te puedes hacer

¿Por qué ((desplazamiento % 26) + 26) % 26 y no solo % 26? Por lo mismo que en la rotación de arrays: el resto de un número negativo es negativo en Java (-1 % 26 da -1), y una posición negativa daría una letra equivocada o un error. Sumar 26 y volver a hacer % 26 lo deja siempre entre 0 y 25.

¿Por qué dos ramas separadas para minúsculas y mayúsculas? Porque 'a' y 'A' tienen códigos distintos y cada tipo de letra tiene que dar la vuelta dentro de su rango. Si mezclaras las dos, una z mayúscula podría convertirse en una letra minúscula.

¿Por qué la ñ y las tildes no se cifran? Porque no están entre 'a' y 'z' en la tabla de caracteres, y el enunciado dice que todo lo demás se deja igual. Para cifrarlas habría que ampliar el alfabeto (por ejemplo, con un String de letras y indexOf).

¿Se puede hacer sin StringBuilder? Sí, con resultado += letra en un String. Para textos cortos funciona igual; con textos largos, StringBuilder es más eficiente porque no crea un String nuevo en cada vuelta.

Olvidar el (char) al añadir al resultado. Si escribes sb.append('a' + (c - 'a' + d) % 26), Java suma números y añade el número (por ejemplo, 107) en vez de la letra. El programa compila y la salida es un montón de cifras.

¿Qué falla aquí?

Tres fragmentos de código con un error de Strings cada uno. Antes de leer la explicación, intenta encontrar el fallo.

Fragmento 1: el anagrama que nunca acierta

static boolean esAnagrama(String a, String b) {
    char[] x = a.toLowerCase().toCharArray();
    char[] y = b.toLowerCase().toCharArray();
    Arrays.sort(x);
    Arrays.sort(y);
    return new String(x) == new String(y);
}

Con esAnagrama("Roma", "amor") devuelve false, aunque las dos palabras tienen las mismas letras. Es el fallo de mi examen.

¿Qué falla? La última línea compara con ==. new String(x) y new String(y) crean dos objetos distintos, aunque contengan "amor" los dos, y == solo mira si es el mismo objeto.

Arreglo: return new String(x).equals(new String(y)); (o Arrays.equals(x, y), comparando directamente los arrays). Con eso, esAnagrama("Roma", "amor") da true.

Fragmento 2: la limpieza que no limpia

static String limpiar(String texto) {
    texto.trim();
    texto.toLowerCase();
    return texto;
}

Con limpiar(" HOLA ") devuelve " HOLA ": exactamente lo mismo que entró.

¿Qué falla? Los String son inmutables: trim() y toLowerCase() devuelven un String nuevo, y aquí ese resultado se tira. texto sigue apuntando al original sin cambios. No hay ningún error al compilar ni al ejecutar, y por eso cuesta tanto verlo.

Arreglo: guardar cada resultado:

texto = texto.trim();
texto = texto.toLowerCase();

Con eso, limpiar(" HOLA ") devuelve "hola".

Fragmento 3: la versión que no se deja dividir

String version = "2.4.1";
String[] partes = version.split(".");
System.out.println(partes[0]);

Se esperaba imprimir 2, pero aparece:

java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0

¿Qué falla? El separador de split es una expresión regular, y en una expresión regular el punto significa «cualquier carácter». Todos los caracteres son separadores, así que todos los trozos salen vacíos, se descartan y el array queda vacío (longitud 0). Por eso partes[0] no existe.

Arreglo: escapar el punto con dos barras: version.split("\\."). Con eso, partes es ["2", "4", "1"] y partes[0] imprime 2.

Errores típicos en estos ejercicios

  • Comparar textos con == en vez de equals (los char sí se comparan con ==).
  • Olvidar guardar el resultado de trim, toLowerCase o replace: los String no cambian.
  • No normalizar (minúsculas y tildes) antes de comparar.
  • Dividir con split(" ") cuando puede haber espacios dobles, o con split(".") sin escapar el punto.
  • No contemplar los casos límite: texto vacío, solo espacios, una sola letra.
  • Olvidar el (char) al construir letras a partir de números.

Chuletario: Strings en Java

Lo esencial en 30 segundos

  • Un String es inmutable: ningún método lo modifica, devuelve uno nuevo. Si no guardas el resultado, no pasó nada.
  • Para comparar contenido, equals, nunca ==.
  • length() con paréntesis (en arrays no).
  • Los índices empiezan en 0; el último es length() - 1.

Comparar

Quiero…CódigoNotas
Igualdad de contenidoa.equals(b)✅ Siempre así
Ignorando mayúsculasa.equalsIgnoreCase(b)"Hola" y "HOLA" → true
Orden alfabéticoa.compareTo(b)<0 antes, 0 igual, >0 después. "a".compareTo("b") → -1
¿Vacío?s.isEmpty() / s.isBlank()isBlank también es true con solo espacios
❌ a == bCompara referencias. A veces da true (literales iguales reutilizados) y a veces false: por eso es traicionero

Comparar con == el resultado de construir o transformar un texto (new String(...), ordenar letras, substring…). Dos textos idénticos pueden ser objetos distintos y == da false.

Consultar y extraer (con s = "Hola, Mundo")

Quiero…CódigoResultado
Longituds.length()11
Un carácters.charAt(0)'H'
Desde una posicións.substring(6)"Mundo"
Un tramo (fin excluido)s.substring(0, 4)"Hola"
Primera posición de…s.indexOf("o")1
Si no estás.indexOf("z")-1
Última posicións.lastIndexOf('o')10
¿Contiene?s.contains("Mun")true
¿Empieza / acaba por…?s.startsWith("Ho") / s.endsWith("do")true

Transformar (siempre devuelven un String nuevo)

Quiero…CódigoResultado
Mayúsculas / minúsculass.toUpperCase() / s.toLowerCase()"HOLA, MUNDO"
Reemplazars.replace('o', '0')"H0la, Mund0"
Quitar espacios de los extremos" x ".trim()"x"
Repetir"ab".repeat(2)"abab"
UnirString.join("-", "a", "b")"a-b"
String limpio = "  HOLA  ".trim().toLowerCase();   // ✅ guardamos el resultado
texto.trim();                                       // ❌ no hace nada visible: se pierde el valor nuevo

Llamar a texto.trim() o texto.toLowerCase() sin asignar. limpiar(" HOLA ") seguía devolviendo " HOLA "; con return s.trim().toLowerCase(); devuelve "hola".

split y conversiones

Quiero…CódigoResultado
Trocear por coma o punto y coma"a,b;c".split("[,;]")[a, b, c]
Trocear por puntos"2.4.1".split("\\.")[2, 4, 1]
Trocear por espacios (varios)" a b ".trim().split("\\s+")[a, b]
String → char[]"hola".toCharArray()[h, o, l, a]
char[] → Stringnew String(chars)"ok"
Número → StringString.valueOf(12)"12"
String → númeroInteger.parseInt("34")34
  • split recibe una expresión regular, no un texto literal. "." en regex significa «cualquier carácter», así que "2.4.1".split(".") devuelve un array vacío, y partes[0] revienta con ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0. Para un punto literal: "\\.".
  • "".split("\\s+") devuelve [""] (longitud 1, no 0). Y si hay un espacio al principio, el primer elemento sale vacío. Por eso se hace trim() antes y se comprueba isEmpty().

Otros caracteres que en regex significan algo y hay que escapar con \\: | * + ? ( ) [ ] { } ^ $.

Caracteres (char)

Quiero…CódigoResultado
¿Es letra?Character.isLetter('a')true
¿Es dígito?Character.isDigit('5')true
¿Letra o dígito?Character.isLetterOrDigit('_')false
A minúsculaCharacter.toLowerCase('A')'a'
Siguiente letra(char) ('a' + 1)'b'
Distancia entre letras'c' - 'a'2
  • Un char es un número por debajo: por eso se puede restar y sumar. Al volver a char hace falta el cast (char).

StringBuilder (para construir texto en un bucle)

StringBuilder sb = new StringBuilder();
for (char c : "abc".toCharArray()) sb.append(c).append('-');
sb.reverse();                    // "-c-b-a"
String resultado = sb.toString();
  • Concatenar con + dentro de un bucle crea un String nuevo en cada vuelta. StringBuilder es mutable y evita ese coste.

Plantillas de los ejercicios

Normalizar (quedarse con letras y dígitos, en minúsculas):

StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray())
    if (Character.isLetterOrDigit(c)) sb.append(Character.toLowerCase(c));
return sb.toString();

Palíndromo (dos punteros):

String t = normalizar(s);
int i = 0, j = t.length() - 1;
while (i < j) {
    if (t.charAt(i) != t.charAt(j)) return false;
    i++; j--;
}
return true;

Normalizar antes: así "Ana" y "Anita lava la tina" son palíndromos. Con "hola" da false.

Contar palabras:

String t = s.trim();
if (t.isEmpty()) return 0;
return t.split("\\s+").length;

" El gato come pescado fresco " → 5

Contar vocales:

int n = 0;
for (char c : s.toLowerCase().toCharArray())
    if ("aeiou".indexOf(c) != -1) n++;

"Canción" → 3 (la ó acentuada no está en "aeiou": añade áéíóú si quieres contarla).

Palabra más larga:

String mejor = "";
for (String p : s.trim().split("\\s+"))
    if (p.length() > mejor.length()) mejor = p;

En el ejemplo anterior → "pescado".

Cifrado César (desplazamiento d, admite negativos y mayores de 26):

int k = ((d % 26) + 26) % 26;                    // normaliza d a 0..25
StringBuilder sb = new StringBuilder();
for (char c : s.toCharArray()) {
    if (c >= 'a' && c <= 'z')      sb.append((char) ('a' + (c - 'a' + k) % 26));
    else if (c >= 'A' && c <= 'Z') sb.append((char) ('A' + (c - 'A' + k) % 26));
    else                           sb.append(c);  // signos, espacios, ñ, acentos: igual
}

cifrar("Hola, Mundo!", 3) → "Krod, Pxqgr!" · descifrar = cifrar con -d · "xyz"+3 → "abc" · "abc"−1 → "zab" · 29 equivale a 3.

Anagrama:

char[] x = a.toLowerCase().toCharArray();
char[] y = b.toLowerCase().toCharArray();
Arrays.sort(x); Arrays.sort(y);
return new String(x).equals(new String(y));      // ✅ equals, no ==

esAnagrama("Roma", "amor") → true.

Errores que más se repiten

SíntomaCausa probable
Comparación que «a veces» funcionaUsas == en lugar de equals
El texto no cambia tras trim()/replace()/toLowerCase()No guardaste el resultado (inmutabilidad)
StringIndexOutOfBoundsExceptioncharAt(length()) o substring con índices fuera de rango
split(".") devuelve array vacío / Index 0 out of bounds for length 0. es regex: usa "\\."
split("\\s+") da 1 palabra en un texto vacío"".split(...) devuelve [""]: trim() + isEmpty() primero
El palíndromo falla con mayúsculas, espacios o signosFalta normalizar antes de comparar
Cifrado con valores negativos da letras raras% en Java puede dar negativo: ((d % 26) + 26) % 26

Antes de entregar, pregúntate

  1. ¿Estoy comparando contenido con equals y no con ==?
  2. ¿Guardo el resultado de cada método que «transforma»?
  3. ¿Qué pasa con un texto vacío, con espacios de más o con mayúsculas?
  4. ¿Mi split usa una regex válida (y escapada)?

Resumen

  • Para palíndromos: limpiar primero (minúsculas, sin tildes, solo letras y dígitos) y comparar con dos posiciones que avanzan una hacia la otra.
  • Para contar palabras: trim(), comprobar si queda vacío y después split("\\s+").
  • Con char se puede operar como con números: c - 'a' da la posición en el alfabeto, y el % 26 hace que dé la vuelta.
  • Con desplazamientos o restos que pueden ser negativos: ((n % m) + m) % m.
  • Los String no cambian: guarda siempre el resultado de los métodos.
  • == compara referencias; equals compara contenido; con char, == está bien.

Checklist: ¿lo has conseguido?

  • ¿Has intentado resolver cada ejercicio antes de mirar la solución?
  • ¿Sabes explicar por qué el palíndromo se comprueba con dos posiciones y no hace falta darle la vuelta al texto?
  • ¿Sabes por qué contarPalabras("") necesita su propia comprobación?
  • ¿Entiendes qué hace el % 26 en el cifrado César y por qué hay que convertir con (char)?
  • ¿Has encontrado los tres fallos de «¿Qué falla aquí?» sin leer la explicación?

Con esto cierras el bloque de Strings. El siguiente paso en Java son las expresiones regulares, que ya has visto asomar en split("\\s+") y en split("\\."). ¡Allí te espero!

Sigue aprendiendoTodos los artículos de Java

¿Te ha servido este artículo? Así puedes apoyar SergioLearns →

Sigue aprendiendo

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *