📝 ¿Qué es un String en Java?
En Java, un String es un objeto que representa una secuencia de caracteres. A diferencia de los tipos primitivos como int o double, String es una clase definida en el paquete java.lang, lo que significa que cada cadena de texto es un objeto con métodos y propiedades. Sin embargo, Java ofrece una sintaxis especial que permite trabajar con Strings casi como si fueran tipos primitivos, creándolos directamente con comillas dobles.
La clase String es una de las más utilizadas en cualquier programa Java. Desde la lectura de datos introducidos por el usuario hasta la construcción de consultas SQL, la manipulación de URLs o el formateo de mensajes de salida, los Strings están presentes en prácticamente todas las aplicaciones. Comprender cómo funcionan internamente —su inmutabilidad, el String Pool y las diferencias entre equals() y ==— es fundamental para escribir código eficiente y libre de errores.
Serializable, Comparable<String> y CharSequence. Esto le permite ser serializada, ordenada naturalmente y utilizada de forma intercambiable con StringBuilder y StringBuffer a través de la interfaz común CharSequence.
🔧 Formas de crear un String
Java ofrece varias maneras de crear objetos String. Las dos principales son el literal de cadena (con comillas dobles) y el constructor new String(). Aunque ambas producen un String funcional, su comportamiento respecto a la memoria es muy diferente.
▶️ Mediante literal de cadena
Es la forma más habitual y recomendada. El compilador almacena el String en el String Pool, una zona especial de memoria que permite reutilizar cadenas idénticas:
String saludo = "Hola, mundo"; String otroSaludo = "Hola, mundo"; // Ambas variables apuntan al MISMO objeto en el String Pool System.out.println(saludo == otroSaludo); // true
▶️ Mediante el constructor new
Con new String() se crea siempre un objeto nuevo en el heap, aunque exista un String idéntico en el Pool:
String s1 = new String("Hola, mundo");
String s2 = new String("Hola, mundo");
// Son objetos DIFERENTES en memoria, aunque tengan el mismo contenido
System.out.println(s1 == s2); // false
System.out.println(s1.equals(s2)); // true
▶️ Desde un array de caracteres
char[] letras = {'J', 'a', 'v', 'a'};
String nombre = new String(letras);
System.out.println(nombre); // Java
// También se puede crear desde una porción del array
String sub = new String(letras, 0, 2); // "Ja"
▶️ Desde un array de bytes
byte[] datos = {72, 111, 108, 97};
String texto = new String(datos, java.nio.charset.StandardCharsets.UTF_8);
System.out.println(texto); // Hola
🏊 El String Pool de Java
El String Pool (también conocido como intern pool o String constant pool) es una estructura de datos especial dentro del heap de la JVM que almacena referencias a objetos String. Su objetivo principal es ahorrar memoria reutilizando cadenas con contenido idéntico en lugar de crear copias independientes.
Cuando el compilador Java encuentra un literal de cadena en el código fuente, comprueba si ya existe un String con el mismo contenido en el Pool. Si existe, reutiliza esa referencia. Si no existe, crea un nuevo objeto String y lo almacena en el Pool. Este mecanismo es posible gracias a la inmutabilidad de los Strings: al no poder modificarse, es seguro compartir la misma instancia entre múltiples variables.
🔹 El método intern()
Los Strings creados con new String() no se añaden automáticamente al Pool. Sin embargo, se puede forzar su inclusión mediante el método intern():
String s1 = "Java";
String s2 = new String("Java");
String s3 = s2.intern();
System.out.println(s1 == s2); // false — s2 está fuera del Pool
System.out.println(s1 == s3); // true — intern() devuelve la referencia del Pool
intern() puede causar problemas de rendimiento, ya que el Pool utiliza una tabla hash interna con tamaño fijo. En aplicaciones con millones de cadenas distintas, es preferible gestionar la deduplicación con estructuras de datos propias (como un HashSet<String>).
🔒 Inmutabilidad de los Strings
Los objetos String en Java son inmutables: una vez creado un String, su contenido no puede cambiar. Cualquier operación que aparentemente modifique un String (como toUpperCase(), concat() o replace()) en realidad crea y devuelve un nuevo objeto String, dejando el original intacto.
String original = "Hola"; String modificado = original.toUpperCase(); System.out.println(original); // Hola (NO ha cambiado) System.out.println(modificado); // HOLA (nuevo objeto) System.out.println(original == modificado); // false
🔹 ¿Por qué son inmutables?
La inmutabilidad de String es una decisión de diseño deliberada del lenguaje Java, motivada por tres razones fundamentales:
| Razón | Explicación |
|---|---|
| Seguridad | Los Strings se usan como parámetros en conexiones de red, carga de clases y apertura de archivos. Si fueran mutables, código malicioso podría alterar una ruta o una URL después de la validación de seguridad. |
| Rendimiento | La inmutabilidad permite el String Pool (compartir instancias) y la caché del hashCode (se calcula una sola vez y se reutiliza), lo que mejora el rendimiento en HashMap y HashSet. |
| Thread-safety | Al no poder modificarse, los Strings son inherentemente seguros entre hilos. Múltiples threads pueden leer el mismo String sin necesidad de sincronización. |
⚙️ Métodos principales de String
La clase String ofrece más de 60 métodos. A continuación se presentan los más utilizados, organizados por categorías, con ejemplos de uso para cada uno.
🔹 Métodos de consulta
| Método | Descripción | Ejemplo | Resultado |
|---|---|---|---|
length() |
Devuelve la longitud de la cadena | "Java".length() |
4 |
charAt(int) |
Devuelve el carácter en la posición indicada | "Java".charAt(0) |
'J' |
isEmpty() |
Comprueba si la cadena tiene longitud 0 | "".isEmpty() |
true |
isBlank() |
Comprueba si está vacía o solo contiene espacios (Java 11+) | " ".isBlank() |
true |
contains(CharSequence) |
Comprueba si contiene la secuencia indicada | "Hola mundo".contains("mundo") |
true |
indexOf(String) |
Posición de la primera aparición (-1 si no existe) | "Java es genial".indexOf("es") |
5 |
lastIndexOf(String) |
Posición de la última aparición | "abcabc".lastIndexOf("abc") |
3 |
startsWith(String) |
Comprueba si empieza con el prefijo dado | "Java".startsWith("Ja") |
true |
endsWith(String) |
Comprueba si termina con el sufijo dado | "archivo.txt".endsWith(".txt") |
true |
🔹 Métodos de transformación
| Método | Descripción | Ejemplo | Resultado |
|---|---|---|---|
toUpperCase() |
Convierte a mayúsculas | "hola".toUpperCase() |
"HOLA" |
toLowerCase() |
Convierte a minúsculas | "JAVA".toLowerCase() |
"java" |
trim() |
Elimina espacios al inicio y al final | " hola ".trim() |
"hola" |
strip() |
Como trim() pero reconoce todos los espacios Unicode (Java 11+) | "\u2000hola\u2000".strip() |
"hola" |
replace(old, new) |
Reemplaza todas las apariciones de una secuencia | "aaa".replace("a", "b") |
"bbb" |
substring(begin, end) |
Extrae una subcadena (begin incluido, end excluido) | "Hola mundo".substring(5, 10) |
"mundo" |
repeat(int) |
Repite la cadena n veces (Java 11+) | "ab".repeat(3) |
"ababab" |
🔹 Métodos de división y unión
// split(): divide un String en un array usando un delimitador (regex)
String csv = "Java,Python,C++,JavaScript";
String[] lenguajes = csv.split(",");
// lenguajes = ["Java", "Python", "C++", "JavaScript"]
for (String lang : lenguajes) {
System.out.println(lang);
}
// String.join(): une elementos con un delimitador
String unido = String.join(" - ", lenguajes);
System.out.println(unido); // Java - Python - C++ - JavaScript
// split() con límite
String datos = "nombre:edad:ciudad:país";
String[] partes = datos.split(":", 3);
// partes = ["nombre", "edad", "ciudad:país"] — solo 3 partes
🔹 Métodos de conversión a arrays
String texto = "Java"; // Convertir a array de caracteres char[] caracteres = texto.toCharArray(); // caracteres = ['J', 'a', 'v', 'a'] // Convertir a array de bytes (UTF-8) byte[] bytes = texto.getBytes(java.nio.charset.StandardCharsets.UTF_8); // bytes = [74, 97, 118, 97]
⚖️ Comparar Strings: equals vs ==
Este es uno de los puntos que más errores causa entre programadores que empiezan con Java. Comprender la diferencia entre == y equals() es absolutamente esencial para evitar bugs difíciles de diagnosticar.
| Operador/Método | Qué compara | Uso correcto |
|---|---|---|
== |
Las referencias (si apuntan al mismo objeto en memoria) | Comparar con null o verificar identidad de objeto |
equals() |
El contenido (carácter a carácter) | Comparar si dos Strings tienen el mismo texto |
equalsIgnoreCase() |
El contenido ignorando mayúsculas/minúsculas | Comparaciones insensibles a capitalización |
compareTo() |
El orden lexicográfico (devuelve int) | Ordenación de Strings |
String a = "Hola";
String b = "Hola";
String c = new String("Hola");
// Con literales: == funciona (mismo objeto en el Pool)
System.out.println(a == b); // true
System.out.println(a.equals(b)); // true
// Con new: == falla (objetos diferentes)
System.out.println(a == c); // false ← ¡CUIDADO!
System.out.println(a.equals(c)); // true ← Correcto
// Comparación ignorando mayúsculas
System.out.println("hola".equalsIgnoreCase("HOLA")); // true
// Orden lexicográfico
System.out.println("abc".compareTo("abd")); // -1 (c < d)
System.out.println("abc".compareTo("abc")); // 0 (iguales)
System.out.println("abd".compareTo("abc")); // 1 (d > c)
== para comparar el contenido de Strings es el error más frecuente de principiantes en Java. Funciona a veces (cuando ambos son literales del Pool) y falla en otros casos, lo que lo convierte en un bug intermitente muy difícil de detectar. Regla de oro: siempre usa equals() para comparar Strings.
NullPointerException, compara el literal primero: "esperado".equals(variable) en lugar de variable.equals("esperado"). Si variable es null, la primera forma devuelve false; la segunda lanza una excepción.
🔗 Concatenación de Strings
La concatenación es la operación de unir dos o más cadenas para formar una nueva. Java ofrece varios mecanismos para concatenar Strings, cada uno con sus ventajas según el contexto.
🔹 Operador +
La forma más intuitiva. El compilador traduce internamente las concatenaciones con + a llamadas a StringBuilder (desde Java 5), por lo que para pocas operaciones fuera de bucles es perfectamente eficiente:
String nombre = "María"; int edad = 28; // Concatenación simple — eficiente fuera de bucles String mensaje = "Hola, " + nombre + ". Tienes " + edad + " años."; System.out.println(mensaje); // Salida: Hola, María. Tienes 28 años.
🔹 Método concat()
String saludo = "Hola".concat(", ").concat("mundo");
System.out.println(saludo); // Hola, mundo
// Nota: concat() solo acepta String. No convierte tipos automáticamente.
// Esto NO compila: "Edad: ".concat(28);
// Sí compila: "Edad: ".concat(String.valueOf(28));
🔹 Peligro: concatenación en bucles
// ❌ INEFICIENTE: crea un nuevo String en cada iteración → O(n²)
String resultado = "";
for (int i = 0; i < 10000; i++) {
resultado += i + ", "; // Cada += crea un objeto nuevo
}
// ✅ EFICIENTE: usar StringBuilder → O(n)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i).append(", ");
}
String resultado2 = sb.toString();
🏗️ StringBuilder y StringBuffer
Cuando se necesita construir o modificar cadenas de forma intensiva, Java ofrece dos clases mutables: StringBuilder y StringBuffer. A diferencia de String, estas clases permiten modificar el contenido interno sin crear objetos nuevos, lo que resulta mucho más eficiente para operaciones de concatenación repetida.
| Característica | String | StringBuilder | StringBuffer |
|---|---|---|---|
| Mutabilidad | Inmutable | Mutable | Mutable |
| Thread-safe | Sí (inmutable) | No | Sí (synchronized) |
| Rendimiento | Bajo en concatenación | Alto | Medio (sincronización) |
| Desde | Java 1.0 | Java 1.5 | Java 1.0 |
| Caso de uso | Cadenas constantes | Concatenación en un hilo | Concatenación multihilo |
🔹 Métodos principales de StringBuilder
StringBuilder sb = new StringBuilder("Hola");
// append(): añade al final
sb.append(" mundo"); // "Hola mundo"
// insert(): inserta en una posición
sb.insert(5, "hermoso "); // "Hola hermoso mundo"
// replace(): reemplaza un rango
sb.replace(5, 13, "bello"); // "Hola bello mundo"
// delete(): elimina un rango
sb.delete(5, 11); // "Hola mundo"
// reverse(): invierte la cadena
sb.reverse(); // "odnum aloH"
// toString(): convierte a String inmutable
String resultado = sb.toString();
// Encadenamiento de métodos (method chaining)
String html = new StringBuilder()
.append("<div>")
.append("<p>Contenido</p>")
.append("</div>")
.toString();
StringBuilder se crea con una capacidad de 16 caracteres. Si sabes de antemano el tamaño aproximado de la cadena resultante, es recomendable indicarlo en el constructor: new StringBuilder(1024). Esto evita redimensionamientos internos del buffer.
🎨 Formateo de Strings
Java ofrece varias formas de crear cadenas con formato. El método más potente es String.format(), que funciona de manera similar a printf() en C.
🔹 String.format()
String nombre = "Ana";
int edad = 25;
double salario = 2850.5;
// %s = String, %d = entero, %f = decimal
String info = String.format("Nombre: %s, Edad: %d, Salario: %.2f€", nombre, edad, salario);
System.out.println(info);
// Salida: Nombre: Ana, Edad: 25, Salario: 2850,50€
// Alineación y ancho
System.out.println(String.format("|%-15s|%5d|", "Producto", 42));
// Salida: |Producto | 42|
// Relleno con ceros
System.out.println(String.format("Código: %05d", 42));
// Salida: Código: 00042
🔹 Especificadores de formato más usados
| Especificador | Tipo | Ejemplo | Resultado |
|---|---|---|---|
%s |
String | String.format("%s", "Hola") |
Hola |
%d |
Entero | String.format("%d", 42) |
42 |
%f |
Decimal | String.format("%.2f", 3.14159) |
3.14 |
%b |
Booleano | String.format("%b", true) |
true |
%c |
Carácter | String.format("%c", 'A') |
A |
%n |
Salto de línea | String.format("Línea 1%nLínea 2") |
(salto de línea del SO) |
🔹 Text Blocks (Java 13+)
Desde Java 13 (estables en Java 15), los Text Blocks permiten escribir Strings multilínea de forma legible, delimitados por triple comilla """:
// Antes de Java 13
String json = "{\n" +
" \"nombre\": \"Ana\",\n" +
" \"edad\": 25\n" +
"}";
// Con Text Blocks (Java 15+)
String jsonModerno = """
{
"nombre": "Ana",
"edad": 25
}
""";
// Se pueden combinar con format
String plantilla = """
Estimado/a %s:
Su pedido #%d ha sido confirmado.
Total: %.2f€
""".formatted("Carlos", 1234, 99.95);
🔄 Conversión entre tipos y String
Convertir entre tipos primitivos (o sus wrappers) y String es una operación constante en la programación Java. Es fundamental conocer los métodos correctos para cada dirección de conversión.
🔹 De tipo primitivo a String
int numero = 42; double decimal = 3.14; boolean flag = true; // Método 1: String.valueOf() — el más recomendado String s1 = String.valueOf(numero); // "42" String s2 = String.valueOf(decimal); // "3.14" String s3 = String.valueOf(flag); // "true" // Método 2: concatenación con cadena vacía String s4 = "" + numero; // "42" — funcional pero menos explícito // Método 3: métodos de las clases wrapper String s5 = Integer.toString(numero); // "42" String s6 = Integer.toString(numero, 16); // "2a" (hexadecimal) String s7 = Integer.toBinaryString(numero); // "101010" (binario)
🔹 De String a tipo primitivo
// Conversión con control de errores
try {
int n = Integer.parseInt("42"); // 42
double d = Double.parseDouble("3.14"); // 3.14
long l = Long.parseLong("1000000"); // 1000000
boolean b = Boolean.parseBoolean("true"); // true
// Nota: parseInt lanza NumberFormatException si la cadena no es válida
int error = Integer.parseInt("abc"); // ¡NumberFormatException!
} catch (NumberFormatException e) {
System.err.println("Error de conversión: " + e.getMessage());
}
🔍 Strings y expresiones regulares
La clase String integra varios métodos que utilizan expresiones regulares (regex) internamente, ofreciendo capacidades avanzadas de búsqueda, validación y transformación de texto sin necesidad de usar directamente las clases Pattern y Matcher.
String texto = "Mi teléfono es 612-345-678 y el de casa 914-222-333";
// matches(): comprueba si el String COMPLETO encaja con el patrón
System.out.println("12345".matches("\\d+")); // true (solo dígitos)
System.out.println("abc12".matches("\\d+")); // false
// replaceAll(): reemplaza TODAS las coincidencias
String sinNumeros = texto.replaceAll("\\d", "*");
System.out.println(sinNumeros);
// Mi teléfono es ***-***-*** y el de casa ***-***-***
// replaceFirst(): reemplaza solo la primera coincidencia
String primerReemplazo = texto.replaceFirst("\\d{3}-\\d{3}-\\d{3}", "[CENSURADO]");
System.out.println(primerReemplazo);
// Mi teléfono es [CENSURADO] y el de casa 914-222-333
// split() con regex
String datos = "nombre: edad ; ciudad";
String[] campos = datos.split("[;:]\\s*");
// campos = ["nombre", "edad", "ciudad"]
// Validación de email (patrón simplificado)
String email = "usuario@dominio.com";
boolean esValido = email.matches("[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}");
System.out.println(esValido); // true
matches(), replaceAll() y split() compilan la expresión regular internamente en cada llamada. Si se van a usar repetidamente con el mismo patrón (por ejemplo dentro de un bucle), es más eficiente precompilar el patrón con Pattern.compile() y reutilizar el objeto Pattern.
🏢 Ejemplo integrador: validador de formularios
El siguiente ejemplo reúne la mayoría de conceptos vistos en el artículo para construir un validador de datos de registro de usuario. Utiliza comparación con equals(), métodos de consulta, expresiones regulares, StringBuilder y conversión de tipos.
public class ValidadorFormulario {
/**
* Valida un nombre de usuario.
* Reglas: 3-20 caracteres, solo letras, números y guiones bajos.
*/
public static String validarUsuario(String usuario) {
if (usuario == null || usuario.isBlank()) {
return "El nombre de usuario no puede estar vacío";
}
String limpio = usuario.trim();
if (limpio.length() < 3 || limpio.length() > 20) {
return "El usuario debe tener entre 3 y 20 caracteres";
}
if (!limpio.matches("[a-zA-Z0-9_]+")) {
return "Solo se permiten letras, números y guiones bajos";
}
return null; // null = sin errores
}
/**
* Valida un email con patrón regex.
*/
public static String validarEmail(String email) {
if (email == null || email.isBlank()) {
return "El email no puede estar vacío";
}
String limpio = email.trim().toLowerCase();
if (!limpio.matches("[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}")) {
return "Formato de email no válido";
}
return null;
}
/**
* Valida que la contraseña sea fuerte.
* Reglas: mín 8 chars, al menos una mayúscula, una minúscula,
* un dígito y un carácter especial.
*/
public static String validarPassword(String password) {
if (password == null || password.length() < 8) {
return "La contraseña debe tener al menos 8 caracteres";
}
StringBuilder errores = new StringBuilder();
if (password.equals(password.toLowerCase())) {
errores.append("- Falta al menos una letra mayúscula\n");
}
if (password.equals(password.toUpperCase())) {
errores.append("- Falta al menos una letra minúscula\n");
}
if (!password.matches(".*\\d.*")) {
errores.append("- Falta al menos un dígito\n");
}
if (!password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':\",.<>?/].*")) {
errores.append("- Falta al menos un carácter especial\n");
}
return errores.length() > 0 ? errores.toString() : null;
}
/**
* Valida una edad proporcionada como String.
*/
public static String validarEdad(String edadStr) {
if (edadStr == null || edadStr.isBlank()) {
return "La edad no puede estar vacía";
}
try {
int edad = Integer.parseInt(edadStr.trim());
if (edad < 18 || edad > 120) {
return "La edad debe estar entre 18 y 120 años";
}
} catch (NumberFormatException e) {
return "La edad debe ser un número entero válido";
}
return null;
}
/**
* Ejecuta todas las validaciones y genera un informe.
*/
public static String validarFormulario(String usuario, String email,
String password, String edad) {
StringBuilder informe = new StringBuilder();
informe.append(String.format("=== Validación de registro ===%n"));
informe.append(String.format("Usuario: %s%n", usuario));
informe.append(String.format("Email: %s%n%n", email));
String[] campos = {"Usuario", "Email", "Contraseña", "Edad"};
String[] errores = {
validarUsuario(usuario),
validarEmail(email),
validarPassword(password),
validarEdad(edad)
};
boolean formularioValido = true;
for (int i = 0; i < campos.length; i++) {
if (errores[i] != null) {
informe.append(String.format("❌ %s: %s%n", campos[i], errores[i]));
formularioValido = false;
} else {
informe.append(String.format("✅ %s: OK%n", campos[i]));
}
}
informe.append(String.format("%nResultado: %s%n",
formularioValido ? "REGISTRO VÁLIDO" : "REGISTRO RECHAZADO"));
return informe.toString();
}
public static void main(String[] args) {
// Caso 1: formulario válido
System.out.println(validarFormulario(
"ana_garcia", "ana@empresa.com", "Segura#2026", "28"));
System.out.println("---");
// Caso 2: formulario con errores
System.out.println(validarFormulario(
"ab", "correo-invalido", "1234", "dieciocho"));
}
}
Salida esperada del programa:
=== Validación de registro === Usuario: ana_garcia Email: ana@empresa.com ✅ Usuario: OK ✅ Email: OK ✅ Contraseña: OK ✅ Edad: OK Resultado: REGISTRO VÁLIDO --- === Validación de registro === Usuario: ab Email: correo-invalido ❌ Usuario: El usuario debe tener entre 3 y 20 caracteres ❌ Email: Formato de email no válido ❌ Contraseña: - Falta al menos una letra mayúscula - Falta al menos una letra minúscula - Falta al menos un carácter especial ❌ Edad: La edad debe ser un número entero válido Resultado: REGISTRO RECHAZADO
🚫 Errores frecuentes con Strings
❌ Error 1: Comparar Strings con ==
String input = scanner.nextLine();
// ❌ MAL: puede fallar si input no está en el Pool
if (input == "salir") { ... }
// ✅ BIEN: compara contenido siempre
if ("salir".equals(input)) { ... }
❌ Error 2: No capturar NumberFormatException
// ❌ MAL: se rompe si el usuario escribe texto
int edad = Integer.parseInt(scanner.nextLine());
// ✅ BIEN: con control de errores
try {
int edad = Integer.parseInt(scanner.nextLine().trim());
} catch (NumberFormatException e) {
System.out.println("Por favor, introduce un número válido.");
}
❌ Error 3: Concatenar en bucle con +
// ❌ MAL: O(n²) — crea un nuevo String en cada iteración
String csv = "";
for (String item : lista) {
csv += item + ",";
}
// ✅ BIEN: O(n) — modifica un buffer mutable
String csv = String.join(",", lista);
// ✅ TAMBIÉN BIEN: con StringBuilder para lógica compleja
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lista.size(); i++) {
if (i > 0) sb.append(",");
sb.append(lista.get(i));
}
String csv = sb.toString();
❌ Error 4: Ignorar que los métodos de String devuelven un nuevo objeto
String nombre = " Ana García "; // ❌ MAL: trim() NO modifica nombre, devuelve un nuevo String nombre.trim(); nombre.toUpperCase(); System.out.println(nombre); // " Ana García " — ¡sin cambios! // ✅ BIEN: asignar el resultado nombre = nombre.trim(); nombre = nombre.toUpperCase(); System.out.println(nombre); // "ANA GARCÍA"
❌ Error 5: NullPointerException al llamar métodos sobre un String null
String valor = obtenerValor(); // puede devolver null
// ❌ MAL: si valor es null, lanza NullPointerException
if (valor.equals("esperado")) { ... }
// ✅ BIEN: literal primero (patrón defensivo)
if ("esperado".equals(valor)) { ... }
// ✅ TAMBIÉN BIEN: comprobación explícita de null
if (valor != null && valor.equals("esperado")) { ... }
✏️ Ejercicios prácticos
Ejercicio 1: Contador de vocales y consonantes
Escribe un método contarLetras(String texto) que reciba un String y devuelva un mensaje indicando cuántas vocales y cuántas consonantes contiene, ignorando espacios, números y caracteres especiales. Las vocales acentuadas (á, é, í, ó, ú) deben contarse como vocales.
Ejemplo: contarLetras("Hola Mundo 2026") → "Vocales: 4, Consonantes: 5"
Ejercicio 2: Palíndromo avanzado
Escribe un método esPalindromo(String texto) que determine si un texto es un palíndromo, ignorando mayúsculas/minúsculas, espacios, tildes y signos de puntuación. Debe normalizar las vocales acentuadas (á→a, é→e, etc.).
Ejemplos:
esPalindromo("Anita lava la tina") → true
esPalindromo("Dábale arroz a la zorra el abad") → true
esPalindromo("Hola mundo") → false
Ejercicio 3: Cifrado César con Strings
Implementa un cifrado César que desplace cada letra un número de posiciones dado. El cifrado debe preservar mayúsculas/minúsculas, no modificar caracteres que no sean letras (espacios, números, puntuación), y funcionar con desplazamientos negativos (descifrado).
Ejemplo: cifrarCesar("Hola Mundo!", 3) → "Krod Pxqgr!"
cifrarCesar("Krod Pxqgr!", -3) → "Hola Mundo!"
Ejercicio 4: Analizador de texto (avanzado)
Crea una clase AnalizadorTexto que reciba un String en el constructor y ofrezca los siguientes métodos: contarPalabras(), palabraMasFrecuente(), promedioLongitudPalabras() y resumen(). Usa split(), StringBuilder, String.format() y colecciones si lo deseas.
❓ Preguntas frecuentes sobre Strings en Java
Las dudas más comunes respondidas de forma clara y directa.
💬 Foro de discusión
¿Tienes dudas sobre Strings en Java? Comparte tu pregunta con la comunidad.
Todavía no hay mensajes. ¡Sé el primero en participar!