¿Qué es un objeto inmutable en Java?

Un objeto es inmutable cuando su estado no puede cambiar después de crearlo. String, Integer y LocalDate lo son. Parece una limitación, pero es lo que permite compartirlos entre hilos sin miedo o usarlos como claves de un mapa. Vemos qué significa en la práctica y cómo hacer los tuyos.

¿Se puede cambiar un objeto inmutable?

No, y por eso los métodos que parecen modificarlo en realidad devuelven otro objeto:

String s = "hola";
String t = s.toUpperCase();
IO.println(s + " / " + t);
salidahola / HOLA

s sigue siendo hola. toUpperCase no tocó la cadena: creó una nueva. Lo mismo hacen replace, trim o plusDays de LocalDate. Si llamas al método y no guardas lo que devuelve, no ha pasado nada.

final no es lo mismo que inmutable

final List<String> lista = new ArrayList<>();
lista.add("se puede");
IO.println("final pero mutable: " + lista);

List<String> fija = List.of("a", "b");
try {
    fija.add("c");
} catch (UnsupportedOperationException e) {
    IO.println("List.of: " + e.getClass().getSimpleName());
}

var original = new ArrayList<>(List.of("x"));
var vista = Collections.unmodifiableList(original);
var copia = List.copyOf(original);
original.add("y");
IO.println("vista: " + vista + "  copia: " + copia);
java Inmutable.javahola / HOLA
final pero mutable: [se puede]
List.of: UnsupportedOperationException
vista: [x, y]  copia: [x]

Tres cosas distintas que se confunden a menudo:

¿Qué impide?¿Inmutable?
final ListQue la variable apunte a otra lista. La lista en sí se puede cambiar.No
Collections.unmodifiableList(x)Que la cambies a través de la vista. Si alguien cambia x, la vista lo refleja (sale [x, y]).No
List.of(…) / List.copyOf(x)Cualquier cambio. copyOf hace una copia propia y no le afecta lo que pase después con x.Sí

Cómo crear una clase inmutable

Desde Java 16, con un record: todos sus campos son final, no tiene métodos que los cambien, y además trae equals, hashCode y toString. Para «modificarlo» se devuelve uno nuevo:

record Punto(int x, int y) {
    Punto mover(int dx, int dy) { return new Punto(x + dx, y + dy); }
}

void main() {
    var p = new Punto(1, 2);
    var q = p.mover(3, 0);
    IO.println(p + " -> " + q);
    var claves = new java.util.HashMap<Punto, String>();
    claves.put(new Punto(0, 0), "origen");
    IO.println(claves.get(new Punto(0, 0)));
}
java Punto.javaPunto[x=1, y=2] -> Punto[x=4, y=2]
origen

Si el record tiene campos que a su vez son mutables, como una lista, hay que copiarlos en el constructor con List.copyOf. Si no, quien te la pasó puede seguir cambiándola. Lo vemos con un ejemplo en las buenas prácticas de Java, en el punto 3.

Por qué importa: las claves de un mapa

De las ventajas que se suelen enumerar, esta es la que más se nota. Un HashMap coloca cada clave según su hashCode en el momento de guardarla. Si la clave cambia después, queda en un sitio que ya no le corresponde:

var mapa = new HashMap<Punto, String>();
var p = new Punto(0, 0);
mapa.put(p, "origen");
p.x = 5;
java MapaMutable.java  —  con un Punto mutableget(new Punto(0,0)): null
get(new Punto(5,0)): null
tamaño: 1, containsKey(p): false

La entrada sigue en el mapa (el tamaño es 1), pero no se puede encontrar ni con la clave vieja ni con la nueva, ni siquiera con el mismo objeto p. Con el record de arriba esto no puede pasar, y por eso new Punto(0, 0) encuentra «origen» sin problema.

Las otras ventajas

VentajaPor qué
Se comparten entre hilos sin sincronizarNadie puede cambiarlos, así que ningún hilo lee un estado a medias
No hace falta copiarlosSe pueden devolver y pasar sin copias defensivas
Siempre están en un estado válidoSe comprueban una vez, en el constructor, y ya no cambian
Son fáciles de probar y de razonarEl mismo objeto da siempre los mismos resultados

El coste es crear un objeto nuevo en cada «cambio». Para objetos pequeños es despreciable; si vas a hacer miles de cambios seguidos sobre algo grande, como un texto que se va construyendo, para eso existen las versiones mutables, como StringBuilder.

Entrada publicada en 2012 y revisada en octubre de 2026. Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio; las salidas están copiadas de esa ejecución.

Primera lección

Texto sobre fondo oscuro: «Java != javascript».
Primera lección
para novatos de
Java

La primera lección para quien empieza con Java: no tiene nada que ver con JavaScript, salvo el nombre y parte de la sintaxis. Si quieres aprender Java desde cero, tienes nuestro curso de Java.

Algunas cosas que solemos decir los programadores

Algunas cosas que solemos decir en nuestra profesión:

  • «Funcionaba en mi computadora. Ven conmigo y velo por ti mismo si no me crees»
  • ¿Quién te dio el usuario admin?  ¿Eres administrador?
  • «No es un error, es sólo otra de sus funciones»
  • «Eso es raro…»
  • «Nunca había hecho eso antes…»
  • «Ayer funcionaba»
  • «¡¿Cómo es posible?!»
  • «Revisaste la conexión de red, configuración?» (Especialmente si la aplicación se vuelve muuuy lenta)
  • «¿Metiste datos erróneos y falló?»
  • «Hay algo extraño en tus datos»
  • «¡No he tocado el código en semanas!»
  • «Has de tener una versión equivocada de librerías»
  • «Debe haber sido una coincidencia desafortunada, así que no molestes»
  • «¡No puedo hacer pruebas unitarias de todo!»
  • «No fue error mio, debe ser algo en la librería Open Source que estamos usando»
  • «Esto funciona, aunque no he escrito ninguna prueba unitaria»
  • «Alguien debe haber movido mi código»
  • «¿Ya revisaste si no tienes virus en tu computadora?»
  • «Bueno a pesar de que no funciona, ¿Como se siente con la aplicación?»
  • «No puedes usar esta versión en este sistema operativo»
  • «¿Qué es lo que quieres hacer metiendo esos datos?»
  • «¿Dónde estabas cuando la aplicación se cayó?»
  • «Estoy seguro de que eso lo arregla»
  • «¿Ya reinició el servidor?»
  • «¿Que versión de JRE/JDK/JVM tienen instalada?»

Tomado del artículo original de javacodegeeks.com

Quince Principios Que Un Ingeniero De Software Debe Seguir

Una lista de quince principios que un Ingeniero de Software quizá debería seguir.

  • Mantén en mente los fundamentos.  Si olvidas los conceptos básicos de los lenguajes de programación estás perdiendo tu conocimiento más fundamental.  Lo cual no es buena idea.
  • Asume siempre el peor escenario.  Si tuviste una educación formal en ciencias de la computación, quizá aprendiste algo acerca de la notación “big-O” que te dice el costo de un algoritmo, si es lineal o cuadrático por ejemplo. Saber cuando un algoritmo no tiene manera de ejecutarse rápidamente es importante.

  • Prueba tu código. Asegúrate de probar tu código.  Dependiendo del método que uses, puedes manejar pruebas en varios niveles.  Pero siempre debes crear tantas pruebas como puedas.
  • No emplees nuevas tecnologías sólo porque son nuevas, úsalas para resolver problemas. Como tecnólogos, tendemos a usar las herramientas más nuevas con la esperanza de arreglar las cosas mágicamente.  Que sea útil es la clave, aunque no sea lo máximo.
  • Leer, y mucho. Si no lees acerca de la industria tarde o temprano quedarás fuera de ella.
  • Prueba nuevas técnicas y tecnologías.  Esto no se contrapone con lo dicho anteriormente. Es necesario probar cosas nuevas para determinar si puede ser útil.  Probar cosas nuevas ayuda a aprender y a mantenerte al tanto de la industria.  Prueba sin poner en riesgo un proyecto crítico.
  • Si falla aprenderás algo.  Si tu código falla al menos aprenderás porque no funciona y refinarás la solución.  En algunos casos, se puede considerar a la falla como un pequeño éxito.
  • Cambia el software.  Algunas veces se necesita simplemente terminar el trabajo, pero se debe estar consiente de la deuda técnica que se adquiere. Si simplemente se entrega software sin remover la deuda técnica de forma continua, a la larga el proyecto será una verdadera pesadilla cuando demasiados errores se hayan acumulado.
  • Hacerlo de la “forma correcta”. Muchos desarrolladores tienen la idea de una «forma correcta» de diseño y desarrollar aplicaciones, pero esto puede no ser lo ideal para el proyecto o puede estar sobrado. Esto parece una contradicción con el punto anterior, pero lo ideal es conservar un balance.
  • Deja el código mejor de lo que lo encontraste. En vez de predicar los beneficios de la refactorización, piensa si quieres mantener una pila de código que se vuelve cada vez peor. Si se limpia un poco cada vez que se modifica entonces no será tan difícil después.
  • Ten en cuenta siempre la concurrencia. Si estas construyendo una aplicación web, y no nos referimos a algo de la escala de Facebook, cosas raras pueden ocurrir con la carga excesiva.  Siempre que se tenga una aplicación con una concurrencia de usuarios mayor a 100 pueden ocurrir cosas raras cuando se lee y se escribe sobre algunos recursos, como en las instancias de HashMaps y esto es solo alguna de las cosas de las que te debes preocupar.
  • La cantidad de almacenamiento puede ser libre, pero la transferencia apesta.  Quizá piensas que escribir todo en disco es una forma excelente de persistir datos. Generalmente lo es, pero si usas el almacenamiento en disco para guardar recursos temporales sin depurar, tu aplicación podría volverse lenta muy rápidamente.  El almacenamiento debería limitarse a aquellos datos que necesiten persistir por largos periodos de tiempo, o cuando los datos no pueden residir en memoria.
  • La memoria no puede llegar tan lejos como se piensa.  Para comenzar, muchos tienen sus aplicaciones y sus bases de datos en el mismo servidor. Esto es perfectamente aceptable hasta que ambos requieran de mucha RAM.  Por ejemplo típicamente se tiene una aplicación con Tomcat a 528MB, no obstante una vez que se tiene que lidiar con el almacenamiento persistente (RDBMS, NoSQL, etc) frecuentemente se puede pasar hasta los 8GB y las cosas pueden ponerse difíciles. Obviamente, esto depende mucho de los usuarios que acceden al sistema y de cuantos datos almacenan.
  • El almacenamiento en cache lo arregla todo hasta que tira al servidor. Si se buscan mecanismos para liberar de consultas excesivas a la base de datos, se termina usando una especie de cache.  El problema es que el cache puede terminar usando mucha más memoria que la que se usa de forma típica, especialmente cuando se tiene que lidiar con datos que dependen del numero de usuarios que acceden. Lo peor de usar un cache es que se puede llegar al punto en que se genere un error OutOfMemory, por mencionar el caso de java.  En este punto, el servidor caerá o dejará de responder y el cache no será de ninguna utilidad y se habrá convertido en parte del problema.
  • Piensa como un consultor.  Con los empleados algunas empresas tienden a cambiar las cosas.  Los plazos se pueden mover, el alcance puede aumentar y el desarrollador tiene que encontrar la manera de cumplir con estas nuevas restricciones.  Es necesario que se deje en claro que los plazos no pueden moverse debido a que el trabajo necesario para hacer las cosas no cambia o que el alcance no puede aumentar sin hacer lo mismo con el número de recursos disponibles.  Los consultores tienden a administrar los proyectos de forma diferente que los empleados y está en estos últimos cambiar esa regla.

Buenas prácticas para el uso de Bases de Datos

Un buen diseño de base de datos facilita el mantenimiento, protege la información y ayuda a que las consultas funcionen bien. Estas recomendaciones sirven como guía: conviene adaptarlas al motor de base de datos, a la aplicación y a las necesidades del proyecto.

Nombres y estructura

1. Usa nombres claros y consistentes

Elige una convención y aplícala a tablas y columnas. Los nombres en singular son una opción habitual: Estudiante en vez de Estudiantes. Usa nombres descriptivos, como FechaInscripcion en lugar de Fecha.

Ejemplo: Estudiante, Curso e Inscripcion, con columnas como FechaInscripcion y EstadoInscripcion.

2. Evita espacios y caracteres difíciles de manejar

Los espacios pueden obligar a escribir los nombres entre comillas o delimitadores, según el motor de base de datos.

Ejemplo: usa FechaNacimiento en vez de Fecha de nacimiento. También puedes usar fecha_nacimiento si esa es la convención elegida.

3. Evita prefijos redundantes

El nombre de un objeto debe describir su propósito. No hace falta indicar que es una tabla si ya se sabe por el contexto.

Ejemplo: usa Escuela en vez de TblEscuela o EscuelaTabla.

4. Define identificadores y relaciones de forma deliberada

Las claves primarias permiten identificar registros y relacionarlos. No todas tienen que ser enteros: elige el tipo que mejor represente el dominio y considera si necesitas una clave natural, una clave generada o ambas.

Ejemplo: EstudianteId puede ser un entero generado por la base de datos. Para un país, un código como MX podría ser una clave natural adecuada.

Tipos de datos y seguridad

5. Guarda las contraseñas como hashes seguros

Nunca guardes contraseñas como texto legible ni como valores que se puedan descifrar. La aplicación debe verificarlas mediante una función de hash diseñada para contraseñas, como Argon2id, bcrypt o scrypt.

Ejemplo: almacena el resultado del hash y sus parámetros. Al iniciar sesión, verifica la contraseña ingresada contra ese valor.

6. Elige tipos de datos adecuados para las claves y los índices

Los tipos compactos y compatibles con las relaciones pueden ayudar al rendimiento, pero el tipo de columna debe corresponder a los datos y las consultas reales. Una columna de texto puede ser una clave o un índice válido cuando el dominio lo justifica.

Ejemplo: usa un entero para EstudianteId. Para un código postal, conserva el texto si puede incluir ceros iniciales o letras.

7. Usa tipos booleanos para valores de verdadero o falso

Usa el tipo booleano que ofrezca el motor de base de datos. En algunos motores se representa internamente como BIT; en otros, como un tipo distinto. El nombre de la columna puede indicar claramente que contiene una condición.

Ejemplo: usa EsActivo o IsActive, con un valor verdadero o falso.

8. Protege el acceso a la base de datos con autenticación y permisos mínimos

Cada aplicación o servicio debe usar una cuenta propia con solo los permisos necesarios. Reserva las cuentas administrativas para tareas administrativas.

Ejemplo: la cuenta de la aplicación puede leer y modificar las tablas que necesita, pero no crear usuarios ni cambiar la configuración del servidor.

Consultas y acceso a los datos

9. Selecciona solo las columnas que necesitas

Evita SELECT * en las consultas de la aplicación. Solicitar campos innecesarios puede aumentar el tráfico y complicar los cambios en el esquema.

Ejemplo: usa SELECT EstudianteId, Nombre FROM Estudiante si la pantalla solo muestra el identificador y el nombre.

10. Evalúa el uso de un ORM según la complejidad del proyecto

Un ORM puede simplificar el acceso a los datos y el mantenimiento. Aun así, conviene revisar las consultas que genera y configurar correctamente las relaciones y la carga de datos.

Ejemplo: usa carga anticipada cuando necesites consultar los cursos de varios estudiantes. Evita cargar colecciones que la pantalla no utiliza. Para consultas especializadas, puede ser más conveniente escribir SQL directamente.

11. Diseña índices a partir de las consultas reales

Los índices pueden acelerar búsquedas, ordenamientos y uniones, pero también ocupan espacio y pueden hacer más lentas las escrituras. Usa planes de ejecución y herramientas de análisis para decidir cuáles crear.

Ejemplo: si las consultas filtran con frecuencia por Correo, evalúa crear un índice sobre esa columna. Si buscan por EscuelaId y ordenan por FechaInscripcion, analiza un índice compuesto para ese patrón.

12. Elige el tipo de índice según el motor y la consulta

No existe una regla universal según la consulta sea por rango o puntual. Influyen el motor, la distribución de los datos y el orden de las columnas indexadas.

Ejemplo: revisa el plan de ejecución para comprobar si un índice sobre FechaInscripcion ayuda a consultar las inscripciones de un periodo.

Integridad, disponibilidad y documentación

13. Haz cumplir la integridad de los datos en la base de datos

Usa claves primarias y foráneas, restricciones CHECK, valores NOT NULL y reglas de unicidad cuando correspondan. La aplicación también puede validar los datos, pero la base de datos debe proteger las reglas fundamentales.

Ejemplo: una clave foránea de Inscripcion.EstudianteId a Estudiante.EstudianteId evita referencias a estudiantes inexistentes.

14. Documenta el esquema y las decisiones importantes

Mantén diagramas entidad-relación, definiciones de tablas, convenciones y notas sobre procedimientos, disparadores y scripts. Actualiza la documentación cuando cambie el diseño.

Ejemplo: documenta por qué EstadoInscripcion tiene ciertos valores válidos y qué proceso actualiza ese campo.

15. Planifica los respaldos y la recuperación ante fallos

Define la frecuencia de las copias, cuánto tiempo se conservarán y cuánto tiempo de interrupción o pérdida de datos es aceptable. Prueba periódicamente la restauración: una copia que no se puede recuperar no es suficiente.

Ejemplo: una aplicación crítica puede requerir respaldos automáticos, replicación y conmutación por error, además de pruebas de recuperación.

16. Separa los componentes cuando la arquitectura y la carga lo justifiquen

Mantener el servidor de base de datos y el servidor web en máquinas distintas puede mejorar el aislamiento, la seguridad y la gestión de recursos. En sistemas pequeños o administrados en la nube, otras arquitecturas pueden ser más adecuadas.

Ejemplo: una aplicación de alto tráfico puede alojar la base de datos en una instancia privada, accesible solo desde los servicios autorizados.

17. Mantén los datos binarios grandes fuera de las consultas habituales

Las imágenes y otros archivos grandes pueden almacenarse en un sistema de archivos o en un servicio de almacenamiento de objetos, guardando en la base de datos solo una referencia. Si deben residir en la base, considera colocarlos en una tabla separada para evitar cargarlos en consultas frecuentes.

Ejemplo: guarda FotoPerfilUrl en Estudiante y almacena el archivo en un servicio de almacenamiento.

Decisiones de diseño

18. Normaliza los datos para reducir duplicación y errores

La normalización ayuda a mantener la coherencia. Si una consulta importante requiere muchas uniones y el rendimiento lo justifica, considera una desnormalización controlada, documentada y mantenida correctamente.

Ejemplo: conserva los datos de Escuela en una tabla propia. Si un informe muy usado necesita datos resumidos, puedes crear una copia que se actualice mediante un proceso definido.

19. Invierte tiempo en el diseño y valida las decisiones

Identifica las entidades, relaciones, reglas y consultas principales antes de construir el esquema. Revisa el diseño con ejemplos de datos y escenarios de cambio.

Ejemplo: antes de crear Inscripcion, define si un estudiante puede inscribirse varias veces en el mismo curso y qué regla debe impedir duplicados

Anti-patrones en Java: leer y escribir XML a mano

Leer un valor de un XML con indexOf y substring, o fabricar el documento sumando cadenas, parece más rápido que usar un analizador. Funciona con el primer ejemplo que pruebas y falla con el tercero. Lo comprobamos con seis casos, todos XML válidos.

Leer a mano

Esta es la versión que suele aparecer, con el error que trae casi siempre:

String extraer(String xml) {
    int inicio = xml.indexOf("<nombre>");
    int fin = xml.indexOf("</nombre>");
    return xml.substring(inicio, fin);
}

Devuelve <nombre>Ana: indexOf da la posición donde empieza la etiqueta, y hay que saltársela. Corregido eso, la probamos con seis documentos que cualquier analizador lee sin problema:

String[] casos = {
    "<persona><nombre>Ana</nombre></persona>",
    "<persona><nombre>Gómez &amp; Hijos</nombre></persona>",
    "<persona><nombre><![CDATA[<b>Ana</b>]]></nombre></persona>",
    "<persona><nombre >Ana</nombre></persona>",
    "<persona>\n  <!-- <nombre>viejo</nombre> -->\n  <nombre>Ana</nombre>\n</persona>",
    "<p:persona xmlns:p=\"urn:x\"><p:nombre>Ana</p:nombre></p:persona>",
};
java AMano.javaoriginal: [<nombre>Ana]
[Ana]
[Gómez &amp; Hijos]
[<![CDATA[<b>Ana</b>]]>]
[a><nombre >Ana]
[viejo]
excepción: Range [7, -1) out of bounds for length 63
CasoResultado a manoQué pasó
SimpleAnaBien
Con una entidadGómez &amp; HijosNo traduce &amp; a &
Con CDATA<![CDATA[…]]>Devuelve el envoltorio en vez del contenido
Con un espacio en la etiquetaa><nombre >Ana<nombre > es válido, pero indexOf no lo encuentra y devuelve basura
Con un comentario delanteviejoLee el nombre que estaba comentado
Con espacio de nombresexcepción<p:nombre> no coincide y el programa se cae

Cinco de seis mal, y lo peor es que solo uno avisa. Los demás devuelven un valor equivocado sin ningún error.

Leer con un analizador

El JDK trae uno, así que no hace falta añadir ninguna dependencia:

String nombre(String xml) throws Exception {
    var f = DocumentBuilderFactory.newInstance();
    f.setNamespaceAware(true);
    var doc = f.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
    var nodos = doc.getElementsByTagNameNS("*", "nombre");
    return nodos.getLength() == 0 ? null : nodos.item(0).getTextContent();
}
java ConParser.java[Ana]
[Gómez & Hijos]
[<b>Ana</b>]
[Ana]
[Ana]
[Ana]

Los seis bien. El analizador traduce las entidades, abre el CDATA, ignora los comentarios y entiende los espacios de nombres (por eso se activa setNamespaceAware y se busca con "*").

Escribir a mano

Construir el documento concatenando tiene el problema contrario: hay caracteres que en XML tienen significado y deben escaparse.

String aMano(String nombre, String atributo) {
    return "<root>"
         + "<nombre atr=\"" + atributo + "\">"
         + nombre
         + "</nombre>"
         + "</root>";
}

Con nombre = "Gómez & Hijos <S.A.>" y un atributo con comillas, el resultado ni siquiera es XML:

salida<root><nombre atr="dijo "hola"">Gómez & Hijos <S.A.></nombre></root>
[Fatal Error] :1:26: Element type "nombre" must be followed by either attribute specifications, ">" or "/>".
al leerlo: Element type "nombre" must be followed by either attribute specifications, ">" or "/>".

La línea [Fatal Error] la escribe el analizador por su cuenta en la salida de errores, antes de lanzar la excepción. Este es el error que verá quien reciba el archivo.

Escribir con el DOM

var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
var root = doc.createElement("root");
var n = doc.createElement("nombre");
n.setAttribute("atr", atributo);
n.setTextContent(nombre);
root.appendChild(n);
doc.appendChild(root);

var t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
var out = new StringWriter();
t.transform(new DOMSource(doc), new StreamResult(out));
salida<root>
  <nombre atr="dijo &quot;hola&quot;">Gómez &amp; Hijos &lt;S.A.&gt;</nombre>
</root>

de vuelta: [Gómez & Hijos <S.A.>] atr=[dijo "hola"]

El &, los < > y las comillas se escapan solos. Al volver a leerlo se recupera exactamente lo que se escribió, y eso es lo que hay que exigir a cualquier forma de generar XML.

La versión original de esta entrada usaba JDOM, una biblioteca que sigue existiendo (JDOM 2) y tiene una API más cómoda que el DOM del JDK. Para documentos pequeños como estos no compensa añadir la dependencia; para leer archivos muy grandes sin cargarlos enteros en memoria, mira SAX.

Entrada publicada en 2011 y revisada en octubre de 2026. Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio; las salidas están copiadas de esa ejecución.

Java y Chuck Norris

Unos cuantos hechos acerca de Chuck Norris, que quizá te convenga saber para tu examen de certificación SCJP.

  • Chuck Norris puede crear clases que son ambas cosas: abstract y final.
  • Chuck Norris no despliega aplicaciones, las mete a patadas en el servidor.
  • Chuck Norris puede usar cualquier clase en java.util.* para matarte y sin leer los javadocs. 
  • Chuck Norris puede golpear tan duro a tu aplicación web que la convierte en una aplicación swing con iconos en forma de cráneos humanos. 
  • Chuck Norris contó el mismo para llegar al valor Float.POSITIVE_INFINITY y demostrar que es correcto, dos veces. 
  • synchronized no protege de Chuck Norris, si él lo necesita lo toma. 
  • Chuck Norris no usa javac, el escribe sus aplicaciones directamente en código binario. 
  • El código java de Chuck Norris nunca necesita optimizarse. 
  • Su código es tan rápido que rompe la velocidad de la luz y durante las pruebas mata a 37 personas en los laboratorios de Sun. 
  • El código de Chuck Norris nunca tiene un error. ¡Nunca! 
  • Cuando alguien intenta usar un método deprecated de Chuck Norris, reciben automáticamente una patada voladora en tiempo de compilación. 
  • El paquete java.lang, originalmente contenía la clase Chuck Norris, pero fue retirada luego de una revisión de diseño y una patada voladora propinada a Bill Joy, el diseñador a cargo. 
  • Chuck Norris no escribe código. El simplemente se sienta frente al monitor y lo mira hasta que obtiene lo que quiere. 
  • El código corre mejor cuando Chuck Norris lo mira. 
  • Si tú obtienes una ChuckNorrisException probablemente morirás. 
  • Los objetos de Chuck Norris puede lanzar patadas voladoras a cualquier objeto privado de otro paquete. 
  • Los niveles de visibilidad son public, default, protected, private y “protectedbyChuckNorris”, ¡No intentes acceder a campos con el último modificador! 
  • Chuck Norris puede dividir por cero. 
  • El Garbage Collector solo pasa sobre el código de Chuck Norris cuando hay cuerpos que recolectar. 
  • Chuck Norris implementa “Indestructible”. Todas las demás criaturas implementan “Killable” 
  • Chuck Norris puede hacer herencia múltiple en java. 
  • El código de Chuck Norris usa generics desde la versión 1.3 de java. 
  • Cuando se ejecuta una clase de Chuck Norris, el CPU la corre al doble de velocidad. 
  • Chuck Norris y Java
  • El código de Chuck Norris no puede ser decompilado, no te molestes en tratar.
🙂

Anti-patrones en Java: concatenar cadenas en un bucle

Ir sumando trozos a una cadena con += dentro de un bucle es de esos errores que no se notan con diez elementos y que, con cien mil, dejan un proceso parado varios segundos. Lo medimos, y vemos de paso que la versión clásica tiene además un fallo con las listas vacías.

El código del problema

record Persona(String nombre) {}

String conMas(List<Persona> personas) {
    String s = "";
    for (Persona p : personas) s += ", " + p.nombre();
    return s.substring(2);
}

Funciona, y con tres nombres devuelve Ana, Luis, Marta. El problema es que las cadenas de Java no se pueden modificar: cada += crea una cadena nueva y copia en ella todo lo que ya había más el trozo nuevo. En la vuelta mil se copian los 999 nombres anteriores; en la diez mil, los 9,999. El trabajo total crece con el cuadrado del número de elementos.

Medido

Comparamos esa función con la versión de StringBuilder que hay más abajo, tomando la mejor de cinco ejecuciones para cada tamaño:

long medir(java.util.function.Supplier<String> f) {
    long mejor = Long.MAX_VALUE;
    for (int r = 0; r < 5; r++) {
        long t = System.nanoTime(); f.get(); mejor = Math.min(mejor, System.nanoTime() - t);
    }
    return mejor;
}
java Medir.javapersonas        +=   StringBuilder
     10,000       70 ms       1.18 ms
     20,000      260 ms       0.47 ms
     40,000     1001 ms       0.62 ms
     80,000     4338 ms       0.91 ms

Los milisegundos dependen de la máquina; lo que hay que mirar es cómo crecen. Cada vez que se dobla la lista, += tarda unas cuatro veces más: 70, 260, 1001, 4338. Es la firma de un algoritmo cuadrático. StringBuilder se queda en torno al milisegundo en todos los casos (a esas escalas el ruido de la medida pesa más que el trabajo, y por eso no crece de forma ordenada).

Y además falla con la lista vacía

El substring(2) del final quita la coma y el espacio que sobran al principio. Pero si la lista está vacía, no hay nada que quitar:

conMas(List.of())  —  salida recortadaException in thread "main" java.lang.StringIndexOutOfBoundsException: Range [2, 0) out of bounds for length 0
	...
	at Concatenar.conMas(Concatenar.java:9)

Es el típico arreglo que funciona en todas las pruebas hasta el día en que llega una lista vacía.

La versión con StringBuilder

String conBuilder(List<Persona> personas) {
    var sb = new StringBuilder();
    for (Persona p : personas) {
        if (sb.length() > 0) sb.append(", ");
        sb.append(p.nombre());
    }
    return sb.toString();
}

StringBuilder sí se puede modificar: va añadiendo al final de un búfer que crece cuando hace falta, así que cada nombre se copia una sola vez. Y la coma se pone antes de cada elemento salvo el primero, con lo que no hay nada que quitar al final y la lista vacía devuelve una cadena vacía.

Se le puede pasar una capacidad inicial, new StringBuilder(personas.size() * 16), para que no tenga que crecer por el camino. Es una mejora pequeña: el crecimiento ya es eficiente, y la diferencia de verdad está entre += y StringBuilder, no entre poner o no la capacidad.

Sin escribir el bucle

Para el caso más común, unir elementos con un separador, ni siquiera hace falta el StringBuilder:

IO.println(String.join(", ", tres.stream().map(Persona::nombre).toList()));
IO.println(tres.stream().map(Persona::nombre).collect(Collectors.joining(", ", "[", "]")));
var sj = new StringJoiner(" | ");
for (Persona p : tres) sj.add(p.nombre());
IO.println(sj);
salidaAna, Luis, Marta
[Ana, Luis, Marta]
Ana | Luis | Marta
Para…Se usa
Unir una lista de cadenas con un separadorString.join(", ", lista)
Unir mientras transformas, con prefijo y sufijoCollectors.joining(sep, prefijo, sufijo)
Ir añadiendo en un bucle con separadorStringJoiner
Construir un texto con formato libre en un bucleStringBuilder

¿Y el + fuera de los bucles?

No hay que tenerle miedo. Una expresión como "Hola, " + nombre + ". Tienes " + n + " mensajes" se resuelve en una sola operación: desde Java 9 el compilador la traduce a una llamada especializada que calcula el tamaño final y copia cada parte una vez. Cambiarla por un StringBuilder escrito a mano solo la hace más difícil de leer. El problema es acumular en un bucle, no el operador.

Entrada publicada en 2011 y revisada en octubre de 2026. Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio; las salidas están copiadas de esa ejecución.