Versiones Java LTS (8-25)
Resumen de las funcionalidades principales acumuladas en cada versión LTS
Hola a todos.
Creo este post con una intención un tanto personal: soy malisimo para recordar fechas. Siempre sé cómo usar una funcionalidad concreta, pero nunca recuerdo si se agregó en Java 11, en la 17 o si ya estaba en la 8. Me he cansado de buscarlo en Google cada vez que tengo que utilizarlas en un proyecto nuevo, así que he decidido recopilarlo todo aquí.
Ya que lo tengo organizado, lo comparto con la comunidad. Es un resumen directo de los puntos clave para ubicarnos rápido en la línea de tiempo de Java y saber qué herramientas tenemos disponibles según la versión del entorno donde nos toque trabajar, incluso sirve también para tomarlo como apuntes de estudio.
Creo este post con una intención un tanto personal: soy malisimo para recordar fechas. Siempre sé cómo usar una funcionalidad concreta, pero nunca recuerdo si se agregó en Java 11, en la 17 o si ya estaba en la 8. Me he cansado de buscarlo en Google cada vez que tengo que utilizarlas en un proyecto nuevo, así que he decidido recopilarlo todo aquí.
Ya que lo tengo organizado, lo comparto con la comunidad. Es un resumen directo de los puntos clave para ubicarnos rápido en la línea de tiempo de Java y saber qué herramientas tenemos disponibles según la versión del entorno donde nos toque trabajar, incluso sirve también para tomarlo como apuntes de estudio.
1. Java 8: El Despertar Funcional (2014)
El punto de inflexión. Aquí Java dejó de ser puramente orientado a objetos para abrazar el paradigma funcional.
Streams y Lambdas
Antes de Java 8, manipular colecciones requería bucles imperativos propensos a errores y difíciles de paralelizar. La API de Stream introduce un modelo declarativo: defines el flujo de datos y las operaciones, y la JVM se encarga de la iteración. Además, facilita el procesamiento paralelo transparente con parallelStream().
List<String> nombres = Arrays.asList("Ana", "Juan", "Pedro", "Alberto", "Alicia");
// Enfoque Moderno: Pipeline declarativo
// 1. filter: Selecciona
// 2. map: Transforma
// 3. sorted: Ordena
// 4. collect: Materializa el resultado
List<String> resultado = nombres.stream()
.filter(n -> n.startsWith("A"))
.map(String::toUpperCase)
.sorted()
.collect(Collectors.toList());
// Resultado: ["ALBERTO", "ALICIA", "ANA"]Optional: Adiós al NullPointerException
El inventor de la referencia nula lo llamó su "error del billón de dólares". Optional<T> es un contenedor que puede o no contener un valor no nulo. Nos obliga a pensar en el caso "vacío" en tiempo de compilación/diseño, en lugar de explotar en tiempo de ejecución.
// API Fluida para manejar ausencias
Optional<User> userOpt = repositorio.buscarPorId(123);
String nombre = userOpt
.map(User::getNombre)
.orElse("Usuario Anónimo");
// Si el usuario existe, obtenemos su nombre. Si no, o si el nombre es null, devuelve el default.Nueva API de Fecha y Hora (java.time)
Bonus Track: Dejamos de sufrir con You are not allowed to view links. You are not allowed to view links. Register or Login or You are not allowed to view links. Register or Login (que no era thread-safe) y Calendar. La nueva API inspirada en Joda-Time es inmutable y clara.
LocalDate hoy = LocalDate.now();
LocalDate pago = hoy.with(TemporalAdjusters.lastDayOfMonth());
long diasParaCobrar = ChronoUnit.DAYS.between(hoy, pago);2. Java 11 (LTS): Estabilidad y Limpieza (2018)
Una versión de consolidación. Se eliminaron módulos de Java EE y JavaFX del core, haciendo el JRE más ligero.
Mejoras en la clase String
Java finalmente añadió métodos utilitarios que llevábamos años importando de librerías externas. Esto reduce la dependencia de commons-lang o Guava para tareas triviales.
String multiline = "\n \n";
System.out.println(multiline.isBlank()); // true (detecta espacios y saltos)
String lines = "Java\nPython\nC++";
lines.lines().forEach(System.out::println); // Stream de líneas
System.out.println("Echo ".repeat(3)); // "Echo Echo Echo "
System.out.println(" Hola ".strip()); // Mejor que trim(), soporta UnicodeInferencia de tipos en variables locales (var)
Introduce "sugar syntax" para reducir la redundancia visual. Importante: Java sigue siendo fuertemente tipado estáticamente; el tipo se infiere al compilar y no cambia en runtime.
// Verbosidad innecesaria
// ByteArrayOutputStream bos = new ByteArrayOutputStream();
// Limpieza con var
var bos = new ByteArrayOutputStream();
// Muy útil en bucles
for (var entry : map.entrySet()) {
// ...
}HttpClient Nativo
Se introduce java.net.http.HttpClient, soportando HTTP/2 y WebSockets de forma nativa, con una API asíncrona moderna basada en CompletableFuture.
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder(URI.create("https://api.dev")).build();
// Llamada asíncrona
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println);3. Java 17 (LTS): Modernización del Lenguaje (2021)
Considerada por muchos el nuevo estándar mínimo. Introduce conceptos para modelar datos de forma precisa.
Records (Data Classes)
Los Records no son solo "clases cortas". Son tuplas nominales transparentes. Su objetivo es modelar datos inmutables. Automáticamente implementan el contrato de equals y hashCode basándose en sus campos, lo que los hace perfectos para usar como claves en Mapas o elementos en Sets.
// Define un DTO inmutable, thread-safe y canónico en una línea
public record Coordenada(double x, double y) {
// Constructor compacto para validaciones
public Coordenada {
if (x < 0 || y < 0)
throw new IllegalArgumentException("Solo cuadrante positivo");
}
}Text Blocks
Facilita la escritura de código políglota (SQL, JSON, HTML dentro de Java). El compilador maneja inteligentemente la indentación incidental.
// SQL legible dentro de Java
String query = """
SELECT
id,
nombre,
email
FROM
usuarios
WHERE
activo = TRUE
AND fecha_registro > ?
ORDER BY fecha_registro DESC
""";Sealed Classes (Clases Selladas)
Permite un control granular sobre la herencia. Puedes definir una interfaz o clase y restringir explícitamente quién puede extenderla. Esto habilita un Pattern Matching exhaustivo en el futuro.
// Solo Circulo y Rectangulo pueden ser Formas
public sealed interface Forma permits Circulo, Rectangulo {}
final class Circulo implements Forma {}
final class Rectangulo implements Forma {}
// final class Triangulo implements Forma {} // Error de compilación4. Java 21 (LTS): La Era de la Concurrencia (2023)
Probablemente la actualización más impactante en términos de rendimiento y escalabilidad desde Java 5.
Virtual Threads (Project Loom)
El modelo tradicional de "un hilo de Java = un hilo del Sistema Operativo" limitaba la escalabilidad. Los Virtual Threads son hilos ligeros (costo ~Bytes vs ~Megabytes) gestionados por la JVM. Esto permite escribir código bloqueante (síncrono) simple que escala como si fuera código reactivo asíncrono complejo.
// Antes, crear 10,000 hilos mataba la aplicación.
// Ahora, es trivial y eficiente.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1)); // Bloqueo barato
return i;
});
});
}Pattern Matching para Switch
Convierte el switch en una herramienta poderosa de extracción de datos y control de flujo basado en tipos. Se integra perfectamente con Sealed Classes para asegurar que cubres todos los casos posibles.
// Lógica de negocio basada en tipos
String procesar(Forma forma) {
return switch (forma) {
// Pattern Matching con "Guarded Pattern" (condición extra 'when')
case Circulo c when c.radio() > 10 -> "Circulo grande: " + c.radio();
case Circulo c -> "Circulo chiquito";
case Rectangulo r -> "Rectangulo de area: " + (r.base() * r.altura());
// No necesitamos 'default' si Forma es Sealed y cubrimos todo
};
}Sequenced Collections
Resuelve la inconsistencia histórica de las colecciones. Introduce interfaces como SequencedCollection, SequencedSet, y SequencedMap.
LinkedHashSet<String> cache = new LinkedHashSet<>();
cache.addFirst("Inicio"); // Nuevo método estándar
cache.addLast("Fin");
String primero = cache.getFirst(); // Adiós a iterator().next()5. Java 25: El Futuro (Preview/Incubator)
Lo que se está cocinando en el OpenJDK para simplificar la curva de entrada y mejorar la flexibilidad.
Module Imports (JEP 476)
Simplifica enormemente el inicio de archivos Java. En lugar de importar clases individuales, importas módulos enteros. Es especialmente útil para clases utilitarias o prototipos.
import module java.base;
// Esto hace disponibles todas las clases públicas de los paquetes
// exportados por el módulo java.base (java.util, java.io, java.nio, etc.)Constructores Flexibles
Históricamente, super() o this() debían ser la primerísima instrucción en un constructor. Esto impedía preparar argumentos o validar estados antes de llamar al padre. Java 25 relaja esta restricción, permitiendo código antes de la llamada al constructor padre, siempre que no se acceda a la instancia (this) que se está creando.
public class UsuarioAvanzado extends Usuario {
public UsuarioAvanzado(String data) {
// Pre-procesamiento antes de llamar al padre
var datosLimpios = desinfectarInput(data);
if (datosLimpios.isEmpty())
throw new ValidationException();
super(datosLimpios); // Llamada diferida
}
}Cualquier sugerencia de modificación es bienvenida. 


