Indices
- JEP 485: Stream Gatherers
- JEP 484: Class-File API
- JEP 483: Ahead-of-Time Class Loading & Linking
- JEP 491: Synchronize Virtual Threads without Pinning
- JEP 490: ZGC: Remove the Non-Generational Mode
- JEP 498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe
- JEP 472: Prepare to Restrict the Use of JNI
- JEP 493: Linking Run-Time Images without JMOD
- JEP 486: Permanently Disable the Security Manager
- JEP 479: Remove the Windows 32-bit x86 Port
- JEP 501: Deprecate the 32-bit x86 Port for Removal
- JEP 496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism
- JEP 497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm
- JEP 475: Late Barrier Expansion for G1
- En Preview
- Conclusión
El 18 de marzo, ¡llegará una nueva versión de Java! Echemos un vistazo a las nuevas características, incluyendo la tan esperada implementación final de los Recolectores de Stream.
El orden de las JEP (Propuestas de Mejora del JDK) presentadas aquí se basa en nuestra evaluación de su interés, más que en su numeración oficial.
JEP 485: Stream Gatherers
Como saben, las operaciones de la API de Stream se dividen en operaciones intermedias que generan un nuevo Stream y operaciones de terminal que crean un resultado o tienen un efecto secundario. Sin embargo, las operaciones de terminal incluyen la función “collector”, que permite crear operaciones personalizadas mediante la implementación de Collector. El conjunto de operaciones intermedias incluye únicamente “map
“, “flatMap
“, “filter
“, “distinct
“, “sorted
“, “peek
” y “sorted
“. Esto es así hasta Java 24, que introduce los Recolectores de Stream.
Los puntos clave de la nueva característica son los siguientes:
- Se ha añadido el nuevo método “gatherer” a java.util.stream.Stream.
- Nueva interfaz java.util.stream.Gatherer, que consta de cuatro métodos:
initializer()
crea un estado intermedio inicial mediante el proveedor.integrator()
gestiona los elementos, utiliza opcionalmente el estado intermedio y envía los resultados a las siguientes etapas del flujo. Se basa en la nueva interfaz funcionalIntegrator
.combiner()
fusiona los estados medianteBinaryOperator
.finisher()
trabaja con el estado intermedio y envía el resultado a las siguientes etapas del flujo una vez procesados todos los elementos. UtilizaBiConsumer
.
- Nueva clase java.util.stream.Gatherers, que proporciona varias implementaciones estándar de Gatherer:
fold()
es similar a la operaciónreduce()
;mapConcurrent
es similar aMap
, aprovechando los hilos virtualesscan
realiza la acumulación incrementalwindowFixed
es una implementación estándar de la ventana fijawindowSliding
es una implementación estándar de la ventana deslizante
Además de crear clases personalizadas que implementan Gatherer
, Java proporciona métodos de fábrica estáticos:
- Gatherer.of(integrator)
- Gatherer.ofSequential(integrator)
Ambos métodos tienen variaciones con diferentes argumentos adicionales en forma de interfaces funcionales (initializer()
, integrator()
, combiner()
y finisher()
), que se mencionaron anteriormente.
JEP 484: API de archivos de clase
En JDK 22 se introdujo una API personalizada para trabajar con archivos de clase. Ahora, con JDK 24, ¡esta API está finalizada!
El rápido desarrollo de Java en los últimos años ha dado lugar a actualizaciones frecuentes y regulares del código de bytes, con el que interactúan herramientas estándar como jlink, jar y otras. Estas utilizan bibliotecas como ASM para esta interacción. Para admitir nuevas versiones de código de bytes, las herramientas deben esperar las actualizaciones de las bibliotecas; sin embargo, las bibliotecas, a su vez, esperan la implementación final de las nuevas versiones de JDK. Esta cadena de dependencias ralentiza el desarrollo y la adopción de nuevas funciones de los archivos de clase.
Aunque esta API puede no ser directamente útil para la mayoría de los desarrolladores, es esencial para varios frameworks y bibliotecas, como Spring e Hibernate, que funcionan con bytecode y usan ASM. El problema radica en que las versiones anteriores de ASM son incompatibles con las nuevas versiones del JDK. Si necesitamos actualizar la versión del JDK en un proyecto, también debemos actualizar ASM; por lo tanto, necesitamos actualizar todo lo que depende de él… bueno, casi todo. Sin embargo, solo queríamos actualizar la versión del JDK.
Exploremos una nueva API. Tras experimentar con ella, hemos creado un ejemplo sencillo que lee campos primitivos de constantes estáticas (se requiere un conocimiento básico de las estructuras de archivos de clase):
public class ClassFileExample {
public static void main(String[] args) throws IOException {
var classFile = ClassFile.of().parse(Path.of("./Main.class"));
for (var field : classFile.fields()) {
var flags = field.flags();
if (flags.has(AccessFlag.STATIC) && flags.has(AccessFlag.FINAL)) {
System.out.printf("static final field %s = ", field.fieldName());
var value = field.attributes().stream()
.filter(ConstantValueAttribute.class::isInstance)
.map(ConstantValueAttribute.class::cast)
.findFirst()
.map(constant -> constant.constant().constantValue().toString())
.orElse("null");
System.out.printf("%s%n", value);
}
}
}
}
Esto puede ser sorprendentemente útil, ya que la reflexión conduce a la inicialización de la clase. Algún día, podríamos profundizar en las consecuencias de esta inicialización no intencionada.
Los desarrolladores familiarizados con ASM pueden notar que los autores decidieron no usar el patrón Visitor debido a las nuevas funciones de Java, en particular la coincidencia de patrones.
JEP 483: Ahead-of-Time Class Loading & Linking
Esta nueva función busca optimizar el tiempo de carga de las aplicaciones. Para lograrlo, Java ahora permite el almacenamiento en caché de las clases cargadas. El proceso de generación y uso de esta caché consta de tres pasos:
- Generación de la configuración de AOT. Ejecute la aplicación con el indicador
-XX:AOTMode=record
y especifique la ruta del archivo de salida mediante-XX:AOTConfiguration=PATH
:
java -XX:AOTMode=record -XX:AOTConfiguration=app.aotconf -jar app.jar
- Generación de la caché con configuración. Cambie el modo de AOT para crear y especifique la ruta de salida de la caché mediante el indicador
-XX:AOTCache=PATH
:
java -XX:AOTMode=create -XX:AOTConfiguration=app.aotconf -XX:AOTCache=app.aot -jar app.jar
- Ejecución de la aplicación usando la caché. Use solo el indicador
-XX:AOTCache=PATH
:
java -XX:AOTCache=app.aot -jar app.jar
Según JEP, el tiempo de carga de un programa simple que usa la API Stream disminuyó de 0,031 segundos a 0,018 segundos (una diferencia del 42%). El tiempo de carga de un proyecto basado en Spring (Spring PetClinic) disminuyó de 4,486 segundos a 2,604 segundos.
JEP 491: Synchronize Virtual Threads without Pinning
Este JEP resuelve el problema del bloqueo de subprocesos de la plataforma al usar subprocesos virtuales en bloques synchronized
. Para comprender el impacto de este cambio, analicemos primero el Proyecto Loom y, más específicamente, los subprocesos virtuales.
Cuando se introdujeron los hilos virtuales en JEP 444, se especificaron dos escenarios en los que no liberarían el hilo de la plataforma que estaban usando al bloquearse:
- El bloqueo ocurre en un bloque
synchronized
- El bloqueo ocurre en métodos nativos, ya sean JNI o funciones foráneas.
Ahora, el primer caso no es válido. Los desarrolladores ahora pueden elegir entre usar la palabra clave sincronizada y el paquete java.util.concurrent.locks
, lo que les permite centrarse únicamente en los requisitos específicos de su tarea.
JEP 490: ZGC: Remove the Non-Generational Mode
El Recolector de Basura Z (ZGC) solía admitir dos modos: Generacional y No Generacional. Dado que el ZGC Generacional es la opción preferida en la mayoría de los casos, los desarrolladores decidieron optimizar la compatibilidad con ZGC deshabilitando uno de los modos, el No Generacional. El indicador ZGenerational ya no se utiliza y, si se utiliza, se mostrará el siguiente mensaje de advertencia:
OpenJDK 64-Bit Server VMW warning: Ignoring option ZGenerational; support was removed in 24.0
JEP 498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe
Si se invocan métodos relacionados con la memoria de sun.misc.Unsafe
, se emitirá la advertencia. Estos cambios se alinean con la transición hacia alternativas modernas como la API VarHandle
y la API de Foreign Function & Memory API
. Además, acercan a Java a la eliminación de los métodos relacionados con la memoria de sun.misc.Unsafe, que ya se han marcado como obsoletos para su eliminación. Esta actualización también anima a los desarrolladores de bibliotecas a migrar a las nuevas API.
JEP 472: Prepare to Restrict the Use of JNI
El uso de la interfaz nativa de Java (JNI) y memoria y funciones externas (FFM) ahora emite una advertencia.
Este es el primer paso para restringir el uso de JNI y FFM. En el futuro, se debería lanzar una excepción para el código. Sin embargo, esto no significa que estas funciones se eliminarán, lo cual sería irónico, ya que FFM se lanzó en Java 22. Este paso es necesario para la política de integridad predeterminada. Simplemente significa que los desarrolladores que habilitan el acceso nativo deben indicar explícitamente que consideran que existen funciones inseguras del JDK.
JEP 493: Linking Run-Time Images without JMOD
El indicador --enable-linkable-runtime
, introducido para las compilaciones del JDK, permite a jlink crear imágenes sin depender de los archivos JMOD del JDK. Esta optimización reduce el tamaño final de la imagen en un 25 %.
Aunque este cambio no afecta directamente a los desarrolladores, es especialmente relevante para los contenedores o al crear imágenes mínimas de tiempo de ejecución. Sin embargo, esta optimización no está habilitada por defecto; cada proveedor de JDK decide si la implementa o no.
JEP 486: Permanently Disable the Security Manager
Los preparativos para deshabilitar java.lang.SecurityManager
comenzaron en Java 17, cuando se marcó como obsoleto para su eliminación debido a su uso poco frecuente y su alto coste de mantenimiento. Veamos ahora los cambios.
El indicador -Djava.security.manager
(en cualquier formato) ya no es compatible y genera un error, excepto para -Djava.security.manager=disallow
.
Llamar a System::setSecurityManager
genera una excepción UnsupportedOperationException
.
Las propiedades del sistema relacionadas con SecurityManager
ahora se ignoran y se ha eliminado el archivo conf/security/java.policy
.
Otros cambios están relacionados con la documentación; por ejemplo, se han eliminado las referencias a SecurityManager
y SecurityException
.
Cabe destacar que las clases y los métodos no se eliminan, sino que se degradan a “vacíos”: devuelven un valor nulo o falso, pasan la solicitud del llamante o generan una SecurityException
o una UnsupportedOperationException
.
JEP 479: Remove the Windows 32-bit x86 Port
Finalmente se descontinuará la compatibilidad con Windows x86 de 32 bits. Esto facilita la infraestructura de compilación y pruebas, liberando recursos que ya no son necesarios para el mantenimiento de la plataforma.
Una de las razones para eliminar esta adaptación es la falta de compatibilidad con virtual threads
, que recurren a los kernel thredas
clásicos. Además, la compatibilidad con la última versión de 32 bits de Windows 10 finalizará en octubre de 2025.
JEP 501: Deprecate the 32-bit x86 Port for Removal
El destino de las demás plataformas de 32 bits es claro: serán eliminadas, pero no en esta versión.
Por lo tanto, Linux sigue siendo la última plataforma compatible con 32 bits. Para compilar la versión de 32 bits, ahora es necesario añadir el indicador --enable-deprecated-ports=yes
:
bash ./configure –enable-deprecated-ports=yes
Sin embargo, se espera la eliminación completa de este puerto a partir de Java 25.
JEP 496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism
Este y el siguiente JEP se centran en la criptografía poscuántica.
La criptografía poscuántica se refiere a la creación de algoritmos criptográficos que serán eficaces incluso después de la llegada de las computadoras cuánticas.
De acuerdo con el estándar FIPS 203, se ha introducido la implementación de ML-KEM para las API KeyPairGenerator
, KEM
y KeyFactory
, concretamente ML-KEM-512
, ML-KEM-768
y ML-KEM-1024
. Ahora podemos generar los pares de claves de la siguiente manera:
KeyPairGenerator generator = KeyPairGenerator.getInstance("ML-KEM-1024");
KeyPair keyPair = generator.generateKeyPair();
JEP 497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm
Como continuación del JEP anterior, según el estándar FIPS 204, se ha añadido la implementación de ML-DSA para las API KeyPairGenerator
, Signature
y KeyFactory
, concretamente ML-DSA-44
, ML-DSA-65
y ML-DSA-87
. Similar al punto anterior, veamos un ejemplo de obtención de una firma adecuada:
Signature signature = Signature.getInstance("ML-DSA");
JEP 475: Late Barrier Expansion for G1
Un JEP final que no afecta directamente a los desarrolladores de Java implica cambios en el recolector de basura Garbage-First (G1), trasladando la implementación de sus barreras a una etapa posterior de compilación JIT de C2. Este ajuste busca simplificar la lógica de las barreras para futuros desarrolladores, a la vez que reduce el tiempo de compilación de C2.
En Preview
Más allá de esta lista de nuevas características, algunos cambios permanecen en el estado de Vista Previa o Experimental.
- JEP 404: Generational Shenandoah (Experimental)
- JEP 450: Compact Object Headers (Experimental)
- JEP 478: Key Derivation Function API (Preview)
- JEP 487: Scoped Values (Fourth Preview)
- JEP 488: Primitive Types in Patterns, instanceof, and switch (Second Preview)
- JEP 489: Vector API (Ninth Incubator)
- JEP 492: Flexible Constructor Bodies (Third Preview)
- JEP 494: Module Import Declarations (Second Preview)
- JEP 495: Simple Source Files and Instance Main Methods (Fourth Preview)
- JEP 499: Structured Concurrency (Fourth Preview)
Conclusión
Puede explorar la lista completa de enlaces del JEP aquí.