El Java Que Todavía No Conoces

Hay un Java más grande afuera del que nos enseñaron en la universidad. Una introducción a la brecha entre los ejercicios del aula y el software real.

kosail avatar
  • kosail
  • 18 min read
Photo by Jayden Sim on Unsplash

Esta serie nació de algo que me pasó a mí mismo: después de varios años usando Java, empecé a encontrarme con código que, aunque seguía siendo Java, se veía completamente distinto al que había aprendido en la universidad.

Si estás en los primeros años de la carrera, probablemente todavía no te has encontrado con muchas de estas cosas. Yo tampoco las conocía cuando empecé.

Pero bien: Tú ya conoces Java

Y probablemente conoces más de que te das cuenta. Sabes leer y escribir datos a la consola y al disco. Sabes como se relacionan los objetos entre clases padres y clases hijas. Cómo ordenar datos en estructuras en memoria, etc. Creo que si te diera instrucciones de un programa que lea nombres, los guarde en una lista y luego los imprima, sabrías en minutos cómo escribirlo. Por ejemplo:

Scanner scanner = new Scanner(System.in);
List<String> lista = new ArrayList<>();

for (int i=0 ; i < 5; i++) {
    System.out.print("Escribe un nombre: ");
    String nombre = scanner.nextLine();
    lista.add(nombre);
}

for (int i=0 ; i < lista.size(); i++) {
    System.out.println(lista.get(i));
}

Lo lees y te parece hasta casi obvio. Es el Java que ya conoces, y es exactamente como se supone que debería verse en este punto. La universidad nos da una base sólida sobre los fundamentos de programación, o al menos lo suficiente para ser capaces de aprender nosotros lo que haga falta. No tienen tanto tiempo ni recursos para enfocarse en detalles menores.

El problema viene en cuando no sabemos qué es lo que necesitamos aprender, en qué orden ni con qué prioridad. y tampoco sabemos por dónde iniciar. Vemos algún repositorio de Java o el código fuente de una librería y nos encontramos con algo como esto:

final var names = users.stream()
                 .filter(User::isActive)
                 .map(User::name)
                 .toList();

O esto:

// (Sacado de los docs oficiales de Spring)
@Target(TYPE)
@Retention(RUNTIME)
@Documented @Configuration @Indexed
public @interface SpringBootConfiguration

Y pensamos, con toda honestidad, ¿qué putas es esto?. Somos capaces de entender un poco, pero no se siente nada familiar. Parece otro lenguaje distinto, y eso me refiero cuando digo que “existe un java” al que todavía no te lo has topado.

¿Y por qué no nos lo enseñan?

No conozco la respuesta. Pero si tuviera que suponer diría que la cuestión es simple: porque venimos empezando. Porque ellos no vienen a “enseñarnos Java”, sino a enseñarnos a programar.

Para hacerlo posible, los conceptos se separan en modulos chiquitos: primero vemos estructuras de control, después objetos, después estructuras de datos, después algoritmos… etc. Y funciona. Separar los conceptos funciona.

La cuestión es que sí aprendemos, pero nos quedamos con las ideas como islas disconexas. A mi parecer, el problema viene de que en las aplicaciones reales muchos de estos conceptos e ideas se entretejen y, así como cuando mezclas dos colores sale un tercero, cuando mezclas dos conceptos o problemas de programación, sale un tercero. Uno que no habías previsto, y para el que la universidad sí te preparó, pero no te lo dijo de forma explícita.

Un problema que en clase parecía sencillo, se vuelve problemático mientras le metes más usuarios, más datos, más procesos y reglas. Más cosas pueden salir mal. Y no es que Java de repente se vuelve más complicado que Python y por eso lo odiemos y saltemos del barco; sino que programar problemas reales es complicado. Cuando empiezas a mezclar colores para hacer una pintura, cuando empiezas a construir aplicaciones y encuentras problemas que no tienes idea como resolver, es cuando las cosas se ponen complicadas. Y a medida el problema crece en extensión, naturalmente, la complejidad crece.

Y muchísima gente antes que nosotros se topó con esa pared, y entonces, programaron las soluciones. Crearon mecanismos que automatizaban las cosas y las añadieron a los lenguajes de programación. De ahí que los lenguajes tengan funciones que no tengamos idea de porqué no nos las enseñaron, y es que vienen desde y para problemas que directamente nunca tuvimos en clase. Y no porque existan ahí listos para usarse significa que el lenguaje es difícil.

Te aviso: Hay problemas extremadamente complejos. Las soluciones que inventó la comunidad son mucho más sencillas para utilizar, pero a veces, igual de complejas de entender.

Por ejemplo: Tienes un backend que vende boletos para el concierto de BTS, y tu sistema puede atender a 1 cliente a la vez. Lo compra, lo guarda y lo descuenta. Fácil, sacaste 100% en la tarea.

Pero en la vida real, más de cien mil armys van a querer comprar un boleto en los primeros minutos de apertura. ¿Cómo te aseguras que una entrada no se venda dos veces mientras atiendes a los cien mil al mismo tiempo?

El problema ya no se trata de if, un ArrayList o de usar camino más corto de Dijkstra. Aparecieron concurrencia, sincronización, transacciones y consistencia. Y resulta que Java ya tiene las herramientas para solucionar esos problemas metidas en la propia libreria estándar, precisamente por gente que estuvo aquí antes que nosotros.

Eso es parte de lo que quiero explorar en esta serie.

Cuando empiezas a escribir software profesional

Yo tenía la idea, la tonta idea, de que cuando uno se hacía un programador profesional, que cuando uno ya trabajaba haciendo cosas grandes y reales ya uno era tan necesario usar los bloques básicos que aprendí en primer semestre. Pensaba que escribiría menos if, o que si usaba muchos new lo que sea, es que yo era un mal programmador porque al tener muchos clientes eso explotaría usando toda la memoria del servidor (esto es debatible, pero dejémoslo para después).

En mis primeros programas en los que intentaba escribir “Java profesional” intencionalmente buscaba sacarle la vuelta a las cosas básicas y escribir código “brillante” (en inglés clever code). Va a sonar, como dicen aquí en México, mamador de mi parte: pero es que me sentía súper cool cuando alguien leía mi código y tenían que preguntarme para entenderlo. Decía yo “uff, no le saben”. Hoy lo veo en retrospectiva y creo que no me funcionaban las neuronas, porque para empezar, ¿qué diablos es el “Java profesional?

Pero no como retórica, en serio. Preguntante antes de seguir leyendo:

¿Qué es la programación profesional? ¿En qué momento puedo decir que algo que escribí es profesional?

Eventualmente me cansé de ese roleplay de jugar a ser un pro, y me empecé a cuestionar si realmente lo era. Hoy tengo un criterio un poquito más establecido, así que desde mi opinión dejame decirte: Los programadores “reales” no dejan de usar las cosas básicas que aprendemos en primer semestre. Los seniors también escriben if y for con bastante frecuencia. La diferencia es que ahora esas estructuras básicas están adentro de un sistema que tiene cientos de decisiones alrededor.

Veamos un ejemplo real de esta transición entre programar algo simple, a algo profesional:

El profesor te dice “hazte un sistema que registre estudiantes”. Tú lo primero que piensas es en crear la clase Estudiante, un menu y guardarlos en un ArrayList (una lista que crece sola cuando se necesita). El sistema jala y todo funciona desde la consola (o usando JOptionPane). Si ahora te pregunto, ¿qué cambiarías de este programa si necesitamos que ahora lo use gente real, gente no técnica?. Pensemos en que tus tios y primos o la gente de tu trabajo, por alguna razón necesita usar tu programa con seriedad.

Yo pensaría que, pues hago el menu más bonito, más amigable. Imprimir mejor los errores, manejar mejor la entrada de datos (explicar “te estoy preguntando por tu numero de telefono, no puedes meter letras we”). Es una respuesta válida. Ahora pensemos que tu sistema lo necesitan usar en todos los campus del Tecnológico de México (según la página oficial al día de hoy de Octubre 2026, hay 567,645 estudiantes activos). Simplemente añadimos un nuevo requerimiento: tu sistema debe poder soportar medio millón de estudiantes registrados. Deja de lado que sea rápido, que guarde los datos al disco duro, lo que sea. Nomás, que funcione.

Si les das tu sistema así como está, probablemente va a explotar si usaste un ArrayList: Los ArrayList crecen, pero no se achican automáticamente. En otras palabras, tu programa es propenso a fugas de memoria (memory leaks) porque aunque borraste a un estudiante, el espacio de la lista no disminuye en memoria y puede y va a seguir creciendo. Tal vez en tu laptop con 8 o 16 GB de RAM funcione, pero no podemos garantizar que las computadoras que usen tu programa en Tijuana, en Tampico o en Mérida tengan también tanta RAM disponible. Pero imaginate que cómo sea que fuese lo arreglaste y que ahora jala perfecto.

Ahora tu programa debería ser capaz de guardar los estudiantes a disco. Y bien, esto es fácil: le añades una base de datos, ¿no? Sin problemas le pones MySQL, PostgreSQL o SQLite, la conectas a tu programa y la gente ya puede guardar los datos (e incluso esto arregla al problema anterior sin que tú lo decidieras, porque ahora ya no necesitas guardar todo en memoria). Ahora, piensa que el campus de Ciudad de Mexico quiere ver los estudiantes de Guadalajara. Pero tu base de datos es local, ¿cómo le haces?

sequenceDiagram box Campus Ciudad de México participant Cliente_CDMX as Programa participant BD_CDMX as BD end box Campus Guadalajara participant Cliente_GDL as Programa participant BD_GDL as BD end Cliente_CDMX<<->>BD_CDMX: Lee y escribe datos Cliente_GDL<<->>BD_GDL: Lee y escribe datos

La primera respuesta es “pues la pongo en un servidor o en la nube”. Ignora el cómo conectaste tu programa a la red, pero pues lo haces y el problema se resuelve. Pero, aparecen otros tres:

  1. Tu programa se conecta directo a la base de datos, y funciona. Pero las bases de datos tienen límites de conexiones, y no puedes conectar a diez mil empleados de todos los campus.
  2. La seguridad de la base de datos y quién puede acceder a ella (autenticación y autorización). Necesitas usuarios y permisos.
  3. La información entre un campus y otro parece no estar sincronizada. Tú vas, revisas y a descubres que en tu sistema, si alguien está viendo los datos de un estudiante, y otra persona lo modifica en ese momento no te das cuenta y trabajas con información vieja (stale data). La secretaria del campus de Ciudad de México se queja que ahora tiene que rehacer las 10 titulaciones que ya trabajó hoy, porque el campus de Guanajuato entregó todos los datos tarde y no dijeron nada para evitar multas (y no hay manera de demostrarlo, porque todavía no hay usuarios en tu sistema).

Entonces:

  1. Creas un programa central que se conecte él solito a la base de datos, y que reciba todas las solicitudes y las maneje en orden (a este programa, se le llama “backend” o “servidor”, porque sirve respuestas como un mesero). Para que pueda ser usado por cientos de empleados al mismo tiempo, le añades hilos de procesamiento simultaneo (concurrencia).
  2. Añades usuarios y permisos a la base de datos, y tu programa central verifica, autoriza y autentica.
  3. Le pones a tu programa central (el servidor o backend) que bloquee el row en la base de datos cuando alguien está usándolo, y que no se pueda actualizar información sensible al mismo tiempo aún si recibe treinta solicitudes de actualización en el mismo segundo (consistencia e idempotencia).
sequenceDiagram participant Cliente_A as Ciudad de Mexico participant Cliente_B as Guadalajara participant Backend as Servidor Backend participant BD_Central as BD Central Note over Cliente_A, Cliente_B: Múltiples usuarios concurrentes Cliente_A->>Backend: Envía petición (API/HTTP) Cliente_B->>Backend: Envía petición (API/HTTP) Note over Backend, BD_Central: Conexión centralizada Backend<<->>BD_Central: Consulta y actualiza datos Backend-->>Cliente_A: Devuelve respuesta Backend-->>Cliente_B: Devuelve respuesta

Tu programa ya es un sistema, puede empezar a ser considerado un software profesional. Tu aplicación de terminal conecta con la base de datos, tiene seguridad, y puede ser usado al mismo tiempo por 500 personas. Dejemos el ejemplo aquí porque todavía hay mucha, mucha más tela para cortar (como qué pasa cuando hay un fallo en tu sistema por 1 usuario que hizo algo mal, ¿qué pasa con la información de todos que estaba siendo procesada en ese momento? ¿Tu servidor es capaz de detectar errores en sí mismo y recuperarse, o necesita que alguien venga un sábado en la noche a leer el stacktrace?).

No quiero que te quedes con la sensación de que no importa lo que hagas, siempre sale un problema nuevo. Ese no es el punto, y toma en cuenta que así son los problemas del mundo real. Esa es la razón por la que las empresas designan equipos enteros a resolver partes concretas de sus productos de software.

El punto es que durante todo este proceso, Java no ha cambiado. Fue el problema el que lo hizo. Las personas más experimentadas probablemente lean esto y noten que ya no hablamos de una cuestión de cómo programar en X o Z lenguaje, sino de diseño de software, de arquitectura de datos y de red. Y ustedes tienen razón. Nunca fue una cuestión de sintaxis, ni de lenguaje, sino una diseño. Y es ese diseño que hay detrás del código, en mi opinión, lo que marca cuando programas profesionalmente.

Como dice Luisina de TodoCode, ser programador no te hace ingeniero de software.

Pero el diseño no es solo de arquitectura, sino también de estilo de código. Esto ya es un tema por sí mismo por lo que no quiero ahondar, pero sí quiero mencionar una sola cosa en respuesta a lo que dije al inicio de la sección: yo creía que escribir código brillante (clever code) era lo de los seniors, pero justo lo contrario. Un verdadero senior, un verdadero profesional, escribe código aburrido, legible, claro y sin sorpresas.

De cualquier manera, para resolver estos problemas necesitas una herramienta, y necesitar conocer tu herramienta. Y en estas situaciones donde tienes un problema y no sabes qué hacer, donde Java en particular se vuelve una navaja suiza.

Java puede hacer mucho más que leer del teclado e imprimir en la consola

Hasta aquí te he hablado de sistemas grandes y servidores, y es fácil pensar que, bueno, entonces Java solo sirve para sistemas empresariales pesados y para conseguir un trabajo normal en alguna empresa del sector. Pero la realidad es que el ecosistema es tan ridículamente amplio que abarca desde microcontroladores hasta motores de videojuegos y cómputo de alto rendimiento.

Java tiene una de las librerías estándar más grandes, robustas y probadas en batalla que hay. Casi cualquier problema común de red, criptografía, concurrencia o estructuras de datos ya tiene una solución adentro del propio JDK sin necesidad de instalar dependencias externas.

NOTA: La librería estándar son todas las funcionalidades que un lenguaje de programación trae ya consigo, y que puedes usar directamente.

Digo esto no como una forma de decirte “este lenguaje es mejor” que cualquier otro, porque no lo es. No existe tal cosa como el mejor lenguaje, porque son simples herramientas (en internet suelen decir que el mejor lenguaje es aquel que te paga las cuentas). No obstante, tenemos la percepción de que los lenguajes de moda son “mejores”. Que Rust es mejor, que Python es mejor.

En esta sección quiero mostrarte de manera rápida y corta que Java puede ser, sorprendentemente, un lenguaje bastante grato de usar para programas pequeños, hobbies o para trabajar sistemas de grado empresarial. El código y los conceptos los vamos a ver en futuros artículos de esta serie, pero por ahora, solo una miradita rápida al mundo que tiene por detrás.

Java tiene la capacidad de construir videojuegos 2D y 3D sin problemas. Ya conoces Minecraft, que está hecho en Java. Pero no es el único. Existe una librería llamada libGDX que te permite, desde Java, utilizar tu GPU (tarjeta gŕafica) para escribir gráficos en la pantalla. Muchos de estos juegos los puedes encontrar en la página oficial.

Lista de juegos de libGDX

Java puede llegar a bajo nivel de una manera impresionante para lo que es. Puedes construir emuladores, como el jGB que es un emulador de GameBoy. Puedes construir robots, y controlar motores y sensores con Raspberry Pi’s usando la librería RPiBotLib, o puedes generar gráficos y experimentar generando arte. También, puedes hacer programas de seguridad o telecomunicaciones que corran en tarjetas SIM como las de teléfono, o como las tarjetas de crédito con NFC.

Java Cards, tomada de PisWords

Foto tomada de Piswords. Créditos a ellos.

Como sabes, la JVM (la máquina virtual de Java) es la que ejecuta el Bytecode. Para que los programas se comporten de la misma manera en un servidor, como en tu laptop, como microcomputadoras, Java maneja la memoria de forma automática. En artículos posteriores vamos a visitar la estructura de memoria de Java, pero por ahora dejémoslo en que utiliza solamente una región llamada “Heap”, que es una memoria dinámica, y que es más lenta que otras partes de la memoria (el famoso “Stack”).

En las últimas versiones de Java (desde la JDK 22), este soporte para controlar la memoria RAM directamente (reservar memoria nativa directa, fuera del Heap) se hizo posible con un proyecto grande que recién culminó, llamado Proyecto Panamá, que buscaba conectar las abstracciones de Java con el poder de librerías en C, C++ y Rust. Puedes ejecutar código de librerías nativas desde tus programas en Java para lograr el máximo desempeño, por ejemplo, en IA local, visión por computadora, etc.

Por otro lado, dejando el lenguaje de lado y viendo las herramientas que vienen con él, es interesante saber que Java viene con un intérprete en tiempo real (abre la consola y mete jshell), también trae un servidor web para que puedas servir archivos de forma rápida (así como en Python tienes python -m http.server 8000, en Java es jwebserver -p 8000). También, la JVM trae incluido un monitor de rendimiento y de memoria incorporado (puedes llamarlo desde terminal escribiendo jconsole)

jwebserver en acción

Como ves, herramientas tiene muchas, y usos tiene muchos. Quizá el problema nunca fue Java. Quizá solo lo hemos llevado usándolo para hacer programas bastante aburridos.

No se trata, tampoco, de tener que aprender todo esto

En la universidad, y en la vida en general, nos han acostumbrado a aprender “por si acaso”: En la uni, a memorizar las herramientas y conceptos de todo lo que puedan evaluarnos en el examen; y en la vida, porque nos ayuda a reducir el miedo de lo incierto. Nos sentimos mejores cuando nos sentimos en control, y nos sentimos en control cuando creemos que sabemos todo lo necesario para atacar un problema por nosotros mismos.

Pero la realidad, mi amiga o amigo, es que en los trabajos no esperan que sepas todo ni te lo van a exigir. Por eso se construyen equipos y se delegan tareas en grupo. Quiero que para esta serie pienses no en que necesitas aprender todo esto que hemos mencionado por si acaso, sino más bien adoptar la perspectiva de “justo a tiempo” (Just-in-time, como la propia JVM). En la realidad, los requerimientos de software de los clientes vienen de imprevisto, sepas o no cómo solucionarlo. Y es ahí donde te pones a investigar cómo funciona aquello que necesitas construir. Pero pues eso: la cuestión es saber qué buscar. Tener una repisa mental, y saber qué herramientas existen, cómo se llaman y dónde están cuando la necesites.

No necesitas volverte un super conocedor de Java para ser un buen programador (yo sí creía esto, y aunque no me arrepiento porque aprendí muchísimo, creo que pude haberle sacado más provecho a estos años. Y es triste, porque sí tuve a alguien que me lo dijo, pero yo no hice caso). Solo necesitas dejar de ver el lenguaje como un examen que tienes que pasar, y empezar a verlo como una caja de piezas y/o herramientas con la que puedes construir lo que se te dé la gana.

Java 8 es más viejo de lo que probablemente te des cuenta

En la universidad nos enseñan una versión base de Java (la versión 8), porque muchísimos sistemas del mundo real todavía lo utilizan y ello maximiza nuestras oportunidades laborales. Pero Java 8 salió en Marzo de 2014 (para que te hagas la idea, fue el año en el que Flappy Bird estaba de moda, y el año que fue el Mundial de la FIFA en Brasil). Han pasado exactamente 12 años (casi 13) desde entonces, y creo que queda un poco sobreentendido que si aprendes Java como si siguiéramos en el 2014, éste te va a parecer mucho peor que otros lenguajes.

Y ciertamente es injusto comparar el Java 8 que nos enseñaron, con Python 3.15 o Typescript 7. Comparamos un carro vocho (en Honduras le llamamos cucarachita, o sea, los Volkswagen Tipo I de 1945) con un Toyota Supra. Naturalmente el Toyota se siente más cómodo de usar, aunque el Volkswagen cumpla con cualquier trabajo de manera indestructible.

Nos enseñan Java 8 porque fue una de las versiones que introdujo cambios y avances gigantescos para aquel momento (cambios de ese nivel de grandes solo se volvieron a ver en Java 11, 17, 21 y recientemente en la 25). Java 8 introdujo Lambdas y Streams, y fue la versión más revolucionaria y estable de su década. Muchísimos sistemas bancarios y gubernamentales siguen corriendo en Java 8 por la misma razón que todavía corren COBOL:

Si funciona, no lo toques.

Puedes aprender Java 8 y trabajar con él. Es una decisión válida y no pasa nada, pero ten en mente que al quedarte ahí, te estás privando de todo lo que hace que programar en Java hoy en día sea cómodo y moderno. No te enseñan que Java se ha recortado muchísimo si así gustas (como declarar variables usando var), ni Records (ya no más escribir 200 líneas de getters y setters), ni los hilos virtuales que hacen de la concurrencia algo mucho más fácil (el famoso Project Loom), ni el nuevo HttpClient, ni Pattern Matching. Te enseñan la versión más verbosa y pesada de la historia del lenguaje, y de nuevo, está bien, porque te hace apreciar las funciones modernas al mismo tiempo que te hace capaz de sacar el trabajo si te requieren escribir Java antiguo.

Y por eso existe esta serie. No para darte otro curso aburrido de sintaxis que podrías leer en los docs de Oracle, ni para que dejes de interesarte por el Java que nos enseña la universidad (son las bases, y las bases siempre van a ser lo más importante).

Solo quiero mostrar que Java sí corre afuera de BlueJ, y que la puerta ya la tienes entreabierta.

En el siguiente artículo veremos la sintaxis del Java moderno, las lambdas, records, modulos, clases selladas, pattern matching, y lo que más me gusta por mucho: el código fuente compacto (compact source files). Hasta entonces.

Comments

comments powered by Disqus
kosail

Written by kosail

I spend most of my time making software, learning things I probably didn't need to learn, and turning small questions into unnecessarily large projects. But I like it, and it makes me happy.

Recommended for You