String y StringBuilder en Java
En esta lección
Casi todos los programas trabajan con texto: nombres de clientes, correos, contraseñas, líneas de un fichero. En Java el texto se guarda en objetos de la clase String, que trae decenas de métodos para buscar, recortar, transformar y comparar. En esta lección verás los métodos que usarás a diario, por qué un String no se puede modificar, cuándo necesitas StringBuilder y cómo dar formato a la salida con printf.
Qué es un String
Un String es una secuencia de caracteres (char). Se escribe entre comillas dobles y cada carácter ocupa una posición, empezando en 0, igual que en los arrays:
D e v S c h o o l
0 1 2 3 4 5 6 7 8
String escuela = "DevSchool";
System.out.println(escuela.length()); // 9
System.out.println(escuela.charAt(0)); // D
System.out.println(escuela.charAt(8)); // l
length()devuelve el número de caracteres. En los strings lleva paréntesis (es un método); en los arrays,lengthva sin ellos.charAt(i)devuelve el carácter de la posicióni. La última posición eslength() - 1; si te pasas, obtienesStringIndexOutOfBoundsException.
Los strings son inmutables
Un objeto String no se puede cambiar una vez creado. Todos los métodos que parecen modificarlo en realidad devuelven un String nuevo y dejan el original intacto:
String nombre = "lucía";
nombre.toUpperCase(); // crea "LUCÍA", pero nadie lo guarda
System.out.println(nombre); // lucía
nombre = nombre.toUpperCase(); // guardamos el nuevo en la variable
System.out.println(nombre); // LUCÍA
Es un error muy común llamar a trim() o replace() y olvidarse de guardar el resultado. ¿Por qué Java lo hace así? Porque un objeto inmutable se puede compartir sin miedo: si dos variables apuntan al mismo "Hola", ninguna puede estropeárselo a la otra. Además permite que Java reutilice los literales iguales para ahorrar memoria.
Métodos de String más usados
Buscar
String correo = "marta.ruiz@devschool.es";
System.out.println(correo.indexOf('@')); // 10
System.out.println(correo.indexOf("school")); // 14
System.out.println(correo.indexOf("gmail")); // -1
System.out.println(correo.lastIndexOf('.')); // 20
System.out.println(correo.contains("@")); // true
System.out.println(correo.startsWith("marta")); // true
System.out.println(correo.endsWith(".es")); // true
indexOf devuelve la primera posición donde aparece lo que buscas, o -1 si no está. lastIndexOf busca desde el final.
Extraer: substring
substring(inicio, fin) devuelve el trozo que va desde inicio incluido hasta fin excluido. Con un solo argumento, llega hasta el final:
String correo = "marta.ruiz@devschool.es";
int arroba = correo.indexOf('@');
String usuario = correo.substring(0, arroba);
String dominio = correo.substring(arroba + 1);
System.out.println(usuario); // marta.ruiz
System.out.println(dominio); // devschool.es
Que el final sea excluido tiene una ventaja: fin - inicio es justo la longitud del trozo. "DevSchool".substring(0, 3) son 3 caracteres: Dev.
Transformar
String texto = " Hola Mundo ";
System.out.println(texto.toUpperCase()); // HOLA MUNDO
System.out.println(texto.toLowerCase()); // hola mundo
System.out.println("[" + texto.trim() + "]"); // [Hola Mundo]
System.out.println("[" + texto.strip() + "]"); // [Hola Mundo]
System.out.println(texto.trim().replace('o', '0')); // H0la Mund0
System.out.println("ja".repeat(3)); // jajaja
trim()ystrip()quitan los espacios del principio y del final.strip()es la versión moderna (Java 11) y también reconoce los espacios Unicode especiales; úsala en código nuevo. ExistenstripLeading()ystripTrailing()para un solo lado.replacesustituye todas las apariciones, de un carácter o de un texto:"a-b-c".replace("-", ", ")daa, b, c.
Dividir y unir
split parte un texto por un separador y devuelve un array. String.join hace lo contrario:
String linea = "Ana;20;Madrid";
String[] partes = linea.split(";");
System.out.println(partes.length); // 3
System.out.println(partes[0]); // Ana
System.out.println(partes[2]); // Madrid
String unido = String.join(" | ", partes);
System.out.println(unido); // Ana | 20 | Madrid
Es la base para leer ficheros CSV, que verás en ficheros.
Cuidado: el argumento de
splites una expresión regular. Para partir por un punto no valesplit("."), porque el punto significa “cualquier carácter”; escribesplit("\\."). Lo mismo con|:split("\\|").
Comprobar si está vacío
System.out.println("".isEmpty()); // true
System.out.println(" ".isEmpty()); // false
System.out.println(" ".isBlank()); // true
isEmpty() solo es true si la longitud es 0. isBlank() (Java 11) también da true si solo hay espacios, que es lo que normalmente quieres al validar un formulario.
Comparar strings
Como viste en operadores, un String es un objeto y == compara si son el mismo objeto, no si tienen el mismo texto:
String a = "java";
String b = new String("java");
String c = "JAVA";
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
System.out.println(a.equals(c)); // false
System.out.println(a.equalsIgnoreCase(c)); // true
| Forma | Qué compara | ¿Cuándo usarla? |
|---|---|---|
a == b | Si son el mismo objeto | Casi nunca con String |
a.equals(b) | Mismo texto, exacto | Siempre para comparar contenido |
a.equalsIgnoreCase(b) | Mismo texto sin mirar mayúsculas | Comandos, respuestas del usuario |
Si una variable puede ser null, escribe primero el literal: "salir".equals(comando). Así, si comando es null, obtienes false en lugar de un NullPointerException.
Ordenar: compareTo
compareTo compara alfabéticamente y devuelve un número: negativo si el primero va antes, 0 si son iguales y positivo si va después.
System.out.println("ana".compareTo("luis") < 0); // true: ana va antes
System.out.println("pera".compareTo("pera")); // 0
System.out.println("Zoe".compareTo("ana") < 0); // true
El último sorprende: compara por código Unicode, y todas las mayúsculas van antes que las minúsculas. Para ignorar mayúsculas existe compareToIgnoreCase.
StringBuilder: texto que se modifica
Como los String son inmutables, cada + en un bucle crea un objeto nuevo y copia todo el texto anterior. Con pocas vueltas no se nota, pero con miles el programa se vuelve lento y gasta mucha memoria:
String resultado = "";
for (int i = 0; i < 5; i++) {
resultado += i + ","; // crea un String nuevo en cada vuelta
}
System.out.println(resultado); // 0,1,2,3,4,
StringBuilder es un texto modificable: va añadiendo sobre el mismo objeto, sin copias.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 5; i++) {
sb.append(i).append(",");
}
sb.setLength(sb.length() - 1); // quita la última coma
String resultado = sb.toString();
System.out.println(resultado); // 0,1,2,3,4
Sus métodos principales:
StringBuilder sb = new StringBuilder("Java");
sb.append(" 21"); // añade al final
System.out.println(sb); // Java 21
sb.insert(0, "Curso de "); // inserta en una posición
System.out.println(sb); // Curso de Java 21
sb.reverse(); // le da la vuelta
System.out.println(sb); // 12 avaJ ed osruC
System.out.println(sb.length()); // 16
A diferencia de String, estos métodos sí modifican el objeto. Cuando termines, conviértelo con toString().
Un uso típico: comprobar si una palabra es un palíndromo.
String palabra = "reconocer";
String alReves = new StringBuilder(palabra).reverse().toString();
System.out.println(palabra.equals(alReves)); // true
Consejo: para unir unas pocas piezas en una línea, usa
+sin miedo; el compilador lo optimiza. UsaStringBuildercuando construyas un texto dentro de un bucle.
Dar formato: printf y String.format
Concatenar con + se complica cuando quieres alinear columnas o limitar decimales. printf usa una plantilla con marcadores que se sustituyen por los valores en orden:
String producto = "Teclado";
int unidades = 3;
double precio = 24.5;
System.out.printf("%s: %d uds. a %.2f euros%n", producto, unidades, precio);
Teclado: 3 uds. a 24.50 euros
| Marcador | Tipo | Ejemplo | Resultado |
|---|---|---|---|
%s | Texto (o cualquier objeto) | "%s", "Ana" | Ana |
%d | Entero | "%d", 42 | 42 |
%f | Decimal (6 decimales) | "%f", 3.5 | 3.500000 |
%.2f | Decimal con 2 decimales (redondea) | "%.2f", 3.456 | 3.46 |
%5d | Entero en 5 huecos, a la derecha | "%5d", 42 | 42 |
%-8s | Texto en 8 huecos, a la izquierda | "%-8s", "Ana" | Ana |
%n | Salto de línea |
String.format funciona igual, pero devuelve el texto en lugar de mostrarlo:
String ticket = String.format("%-10s%6.2f", "Pan", 1.2);
System.out.println("[" + ticket + "]"); // [Pan 1.20]
Con anchos fijos puedes alinear una tabla:
String[] productos = {"Pan", "Leche", "Aceite"};
double[] precios = {1.2, 0.95, 8.75};
for (int i = 0; i < productos.length; i++) {
System.out.printf("%-8s%6.2f%n", productos[i], precios[i]);
}
Pan 1.20
Leche 0.95
Aceite 8.75
Cuidado:
printfusa el idioma del sistema. En un ordenador en español,%.2fmuestra coma decimal (24,50). Si necesitas siempre punto, usaString.format(Locale.US, ...).
Text blocks: texto de varias líneas
Desde Java 15 puedes escribir textos largos entre triples comillas, respetando los saltos de línea y sin escapar las comillas:
String json = """
{
"nombre": "Ana",
"curso": "Java"
}
""";
System.out.print(json);
{
"nombre": "Ana",
"curso": "Java"
}
Java elimina la sangría común a todas las líneas, tomando como referencia la posición de las comillas de cierre. Son ideales para JSON, HTML o consultas SQL, como las que escribirás en JDBC.
La clase Character
Para analizar un texto carácter a carácter, la clase Character tiene métodos que clasifican un char:
String clave = "Java2026!";
int letras = 0, digitos = 0, otros = 0;
for (int i = 0; i < clave.length(); i++) {
char c = clave.charAt(i);
if (Character.isLetter(c)) {
letras++;
} else if (Character.isDigit(c)) {
digitos++;
} else {
otros++;
}
}
System.out.println(letras + " letras, " + digitos + " dígitos, " + otros + " otros");
// 4 letras, 4 dígitos, 1 otros
Otros útiles: Character.isUpperCase(c), Character.isLowerCase(c), Character.isWhitespace(c), Character.toUpperCase(c) y Character.isLetterOrDigit(c).
También puedes recorrer un texto con for-each convirtiéndolo antes en array: for (char c : clave.toCharArray()).
Errores frecuentes
- No guardar el resultado:
texto.trim();no cambiatexto. Escribetexto = texto.trim();. - Comparar con
==: usaequals. - Confundir
lengthylength(): arrays sin paréntesis, strings con ellos. - Salirse del texto:
charAt(texto.length())lanzaStringIndexOutOfBoundsException. split("."): el punto es especial; usasplit("\\.").- Concatenar en bucles largos: usa
StringBuilder. - Llamar a un método sobre
null:String s = null; s.length();lanzaNullPointerException.
Resumen
| Necesito… | Método |
|---|---|
| Longitud / un carácter | length(), charAt(i) |
| Buscar | indexOf, lastIndexOf, contains, startsWith, endsWith |
| Extraer | substring(inicio, fin) |
| Transformar | toUpperCase, toLowerCase, strip, trim, replace, repeat |
| Dividir / unir | split, String.join |
| Comprobar vacío | isEmpty, isBlank |
| Comparar | equals, equalsIgnoreCase, compareTo |
| Construir en bucles | StringBuilder: append, insert, reverse, toString |
| Formatear | printf, String.format |
Recuerda que los String son inmutables: sus métodos devuelven textos nuevos. Para convertir un texto en número ("25" en 25) sigue con Math, conversiones y clases envoltorio.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.