La profecía JAVA

Código Java sobre papel antiguo con un calendario maya: un bucle infinito que lanza una EndOfWorldException el 21 de diciembre de 2012 a las 21:02.

Una broma de diciembre de 2012, cuando se decía que el calendario maya predecía el fin del mundo. El programa comprueba la fecha sin parar y lanza una excepción en el momento señalado. Como se ve, la excepción nunca llegó.

Consejo

Texto sobre papel antiguo: «Primero resuelve el problema, después escribes el código», cita de John Johnson.

«Primero resuelve el problema, después escribes el código» -John Johnson

Escribir código es la última parte. Antes hay que entender el problema y tener claro cómo resolverlo; si no, el código solo sirve para descubrir que no lo entendías.

Algunos consejos sobre el uso de Eclipse

Evita los problemas de instalación

Nunca instales una versión nueva de Eclipse sobre una versión  antigua.  Renombra la instalación antigua para quitarla del camino y descomprime la versión nueva en un directorio limpio.

Recupera tu workspace dañado

Un workspace dañado es una causa común de problemas entre muchos desarrolladores. Si tu instalación de 
eclipse comienza a tener errores o un comportamiento inestable, puede que sea tiempo de darle un nuevo comienzo. Ejecuta 
eclipse con la opción -clean y todo los datos en cache y de ejecución serán eliminados.  Esto a menudo ayuda a resolver algunos problemas con plug-ins y mejora la estabilidad en general.

Incrementa el espacio de memoria

Cuando se agregan nuevos plugins al nucleo de funcionalidad de 
eclipse  o cuando se necesita usar plug-ins de terceros, los requerimientos de memoria de eclipse se incrementan.  La memoria default configurada para eclipse deja de ser suficiente para muchos proyectos de tipo empresarial y eso causa una respuesta lenta por parte del IDE.  Si comienzas a tener errores de falta de memoria o respuesta lenta, quizá debas incrementar los valores por default establecidos en el archivo eclipse.ini en el directorio de instalación.  En particular, si llegas a tener un error del tipo «PermGem» (permanent generation), debes incluir esta línea al final del archivo y reiniciar eclipse

XX:MaxPermSize=256m

Usa la menor cantidad de memoria menor que puedas pero que sea suficiente para ejecutar bien tus proyectos.

Edicion Side by side

Puedes editar un mismo archivo desde dos puntos diferentes usando el comando Window>New Editor. Es mas cómodo y mejora la productividad.

Deja que mejore tu código automáticamente

Configura Eclipse para que formatee el código automáticamente y organice los «imports» cada vez que salvas.  Usa la opción en Window>Preferences>Java Editor>Save Actions para habilirtar estas opciones. Esta ventana de dialogo te permitirá configurar acciones como la de remover «casts» incesarios o agregar notaciones faltantes.  Configura tu eclipse para optimizar la compilación, formateo y estilo de código.

Utiliza los teclas de aceso directo

Esto mas productivo y conveniente usar las teclas de acceso directo para ejecutar algunas tareas en eclipse en lugar de buscar las opciones que hacen lo mismo en los menús de navegación.  Por ejemplo al buscar las referencias de una variable, método o una clase pueden ser mas fácilmente encontradas mediante el acceso directo Ctrl+Shif+g.  Busca alguna ficha tácnica con todos los accesos directos de la IDE, son muy comunes y fáciles de encontrar.

via: javacodegeeks.com

¿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.

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.
🙂