Introduction
As we all know, Java is a constantly evolving programming language. With each new release, we get a plethora of new features, enhancements, and sometimes, a few deprecations. In this blog post, we will analyse the significant differences between Java 17 and Java 21 API. We will navigate through the changes, with a focus on deprecated features, new library additions, security enhancements, and performance improvements.
Released on September 19, 2023, Java 21 is celebrated for its comprehensive set of specifications that define the behaviour of the Java language, API, and virtual machine. It represents a Long Term Support (LTS) version, ensuring extended updates and support from various vendors, making it a pivotal release for developers and organizations alike.
Overview of Key API Changes from Java 17 to Java 21
Deprecated/Removed Features
One of the key aspects of Java’s evolution is the deprecation of features that are no longer relevant or have been replaced by more efficient alternatives. In the transition from Java 17 to Java 21, several features, methods and classes have been deprecated or removed. These are the following:
ObjectOutputStream.PutField
write(ObjectOutput)
– Marked for removalEnum.finalize()
– Deprecated and marked for removalRuntime.exec(String)
– DeprecatedRuntime.exec(String, String[])
– DeprecatedRuntime.exec(String, String[], File)
– DeprecatedRuntime.runFinalization()
– Deprecated and marked for removalSystem.runFinalization()
– Deprecated and marked for removalThreadDeath
– Deprecated and marked for removalThreadGroup.allowThreadSuspension(boolean)
– RemovedThread.countStackFrames()
– RemovedThread.getId()
– DeprecatedThread.resume()
– RemovedThread.stop()
– Marked for removalThread.suspend()
– RemovedURL(String)
– DeprecatedURL(String, String, String)
– DeprecatedURL(String, String, int, String)
– DeprecatedURL(String, String, int, String, URLStreamHandler)
– DeprecatedURL(URL, String)
– DeprecatedURL(URL, String, URLStreamHandler)
– Deprecatedjava.security.spec.PSSParameterSpec.DEFAULT
– DeprecatedPSSParameterSpec(int)
– DeprecatedMemoryMXBean.getObjectPendingFinalizationCount()
– Deprecated
Also the following Classes were removed
ClassSpecializer.Factory
ClassSpecializer.SpeciesData
Compiler
FdLibm.Cbrt
FdLibm.Hypot
FdLibm.Pow
MLetContent
MLet
PrivateMLet
MLetMBean
javax.management.remote.rmi.RMIConnector.getMBeanServerConnection(Subject)
javax.management.remote.RMIIIOPServerImpl
javax.management.remote.JMXConnector.getMBeanServerConnection(Subject)
Replacements for Deprecated/Removed Features
Deprecated and Marked for Removal:
- ObjectOutputStream.PutField (No Replacement): While there’s no direct replacement, consider using a custom serialization mechanism or a library like Kryo if object serialization is crucial.
- write(ObjectOutput) (Potential Replacement): For custom serialization scenarios, explore alternatives like writeObject(Object) or writeUnshared(Object).
- Enum.finalize(), Runtime.runFinalization(), System.runFinalization(), ThreadDeath (No Replacement): Finalization is deprecated and might be removed in future versions. Implement proper resource management (e.g., using try-with-resources) to avoid relying on finalization for cleanup.
Deprecated Methods with Alternatives:
- Runtime.exec(String), Runtime.exec(String, String[]), Runtime.exec(String, String[], File): Use ProcessBuilder
ProcessBuilder processBuilder = new ProcessBuilder(command, arguments); Process process = processBuilder.start(); // Handle process output and errors as needed
- Thread.getId() (Use getName(), prefer isAlive() for checking thread status):
String threadName = Thread.currentThread().getName(); // Get current thread name boolean isThreadAlive = Thread.currentThread().isAlive(); // Check if thread is alive
- URL(String), URL(String, String, String), etc. (Use URI or new URL(String, URLClassLoader)):
For constructing URLs with a specific class loader:
URL url = new URL("https://example.com/", MyClassLoader.class.getClassLoader());
Removed Classes/Methods (No Direct Replacements):
- ClassSpecializer.Factory, ClassSpecializer.SpeciesData, Compiler, FdLibm.* (Math functions), MLet*, javax.management.remote.* (RMI-related classes): These are internal or legacy classes that are no longer supported. If you rely on these functionalities, you might need to explore alternative libraries or workarounds depending on your specific use case.
New Features
Java 21 introduces several new features
- transferTo(OutputStream): Java 21 introduces the a method, facilitating efficient data transfer between input and output streams without the need for an intermediary buffer in user space, thereby improving I/O efficiency
- FileInputStream & SequenceInputStream: Similar to `BufferedInputStream`, these classes now include the `transferTo(OutputStream)` method, streamlining the process of transferring data directly to an output stream.
Console and PrintStream Enhancements
Console
Transitioning from `final` to `sealed`, this change in the Console class introduces more controlled subclassing, allowing developers to extend functionality within a restricted hierarchy
PrintStream
The introduction of the `charset()` method in `PrintStream` allows developers to ascertain the charset used by the PrintStream, enhancing support for internationalization.
Serialization Improvements
- InvalidClassException & InvalidObjectException: Java 21 enhances exception handling in serialization with new constructors in these exceptions, enabling more detailed error reporting by including a cause for the exception.
- ObjectInputStream.GetField: The `get(String, Object)` method now throws `ClassNotFoundException`, improving error handling during deserialization by explicitly indicating class-related issues.
Reflection and Access Flags
- Executable & Field: The addition of the `accessFlags()` method in reflective classes like `Executable` and `Field` allows developers to retrieve access flags of classes, methods, or fields at runtime, offering deeper insights into runtime behavior.
- Parameter: Reflecting the changes in `Executable` and `Field`, the `Parameter` class now includes an `accessFlags()` method, further aligning with the enhanced reflective capabilities introduced in Java 21.
Unicode Enhancements in `Character` Class
- Emoji Support: Java 21 significantly expands its support for Unicode, introducing methods like `isEmoji(int)`, `isEmojiComponent(int)`, and `isEmojiPresentation(int)` in the `Character` class. These methods enable applications to better handle, validate, and render emoji characters, catering to modern digital communication standards.
Foreign Function & Memory API (Preview)
- java.lang.foreign: Marked as a preview feature, this package is a significant addition in Java 21, laying the groundwork for improved interaction between Java programs and native code/libraries. This API simplifies the process of invoking foreign functions and managing native memory, promising to enhance performance and reduce the complexity of native code integration .
Usage Scenarios
Let’s see some specific usage scenarios where the new features and API changes introduced in Java 21 can be particularly beneficial. These scenarios will illustrate how developers can leverage these enhancements to address real-world development challenges.
Usage Scenario 1: Efficient Data Transfer in Network Applications
Scenario: A developer is working on a network application that requires the efficient transfer of large files from a server to multiple clients. The application needs to maximize throughput and minimize latency.
Java 21 Solution: Utilize the `transferTo(OutputStream)` method in classes such as `BufferedInputStream` for efficient data transfer. This method allows for direct transfer of bytes between streams, reducing the overhead of copying data to an intermediate buffer.
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class FileServer { public static void main(String[] args) throws IOException { try (ServerSocket serverSocket = new ServerSocket( /* port number */ ); Socket clientSocket = serverSocket.accept(); BufferedInputStream fileInput = new BufferedInputStream(new FileInputStream("largeFile.bin")); OutputStream clientOutput = clientSocket.getOutputStream()) { fileInput.transferTo(clientOutput); System.out.println("File transferred successfully to client."); } catch (IOException e) { e.printStackTrace(); } } }
Benefits: This approach minimizes the I/O overhead and system resource utilization, leading to faster file transfers and improved application performance.
Usage Scenario 2: Advanced Debugging with Reflective Access Flags
Scenario: A development team is building a custom framework that relies heavily on reflection for dynamic operation. They need a way to debug and validate the access levels of various members (methods, fields, etc.) at runtime to ensure correct behavior.
Java 21 Solution: Use the `accessFlags()` method from reflective classes (`Method`, `Field`) to get detailed access level information. This can help in logging, debugging, or runtime validation of application components.
import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class MyClassInspector { public static void main(String[] args) throws ClassNotFoundException { Class<?> clazz = Class.forName("MyClass"); // Replace "MyClass" with the actual class name Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { int flags = method.getModifiers(); System.out.printf("Method: %s, Access Flags: %s%n", method.getName(), Modifier.toString(flags)); } } }
Benefits: This capability enhances debugging and introspection, allowing developers to programmatically check and handle access levels, leading to more robust and secure applications.
Usage Scenario 3: Internationalization and Emoji Support
Scenario: A social media platform aims to enhance its text processing capabilities to better support emojis, ensuring that user-generated content including emojis is correctly validated and displayed across different devices.
Java 21 Solution: Leverage the expanded Unicode and emoji support in the `Character` class to identify, validate, and process emojis within user content.
String userComment = "Hello world!"; userComment.codePoints().forEach(cp -> { if (Character.isEmoji(cp)) { System.out.printf("Found an emoji: %s%n", new String(Character.toChars(cp))); } });
Benefits: By accurately processing emojis, the platform can enhance user experience, ensuring correct display and interaction with emojis across various devices and locales, thereby supporting global user engagement. ### Conclusion The scenarios outlined above demonstrate the practical application of Java 21’s new features in addressing common development challenges. Whether it’s through more efficient data handling, improved debugging and validation mechanisms, or enhanced support for global character sets and emojis, Java 21 provides developers with powerful tools to build more efficient, secure, and user-friendly applications. As Java continues to evolve, staying abreast of these changes and understanding how to apply them in real-world contexts is crucial for developers looking to maximize their productivity and leverage the full power of the Java platform.
The Generational Z Garbage Collector (ZGC)
The Generational Z Garbage Collector (GenZGC) in JDK 21 represents a significant evolution in Java’s approach to garbage collection. It builds upon the Z Garbage Collector (ZGC) by introducing a generational approach, aiming to enhance application performance through more efficient memory management.
Generational ZGC leverages the “weak generational hypothesis,” which suggests that most objects die young. By dividing the heap into young and old regions, GenZGC can focus on the young region where most objects become unreachable, thereby optimizing garbage collection efficiency and reducing CPU overhead. This division allows for more frequent collection of short-lived objects while reducing the overhead of collecting long-lived objects. Internal performance tests have shown that Generational ZGC offers about a 10% improvement in throughput over its single-generation predecessors. A crucial advantage is its ability to mitigate allocation stalls, which significantly benefits high-throughput applications.
While JDK 21 introduces Generational ZGC, single-generation ZGC remains the default for now. Developers can opt into using Generational ZGC through JVM arguments. The plan is for Generational ZGC to eventually become the default. Tools like GC logging and JDK Flight Recorder (JFR) offer valuable insights into GC behavior and performance for those evaluating or transitioning to Generational ZGC.
Generational ZGC represents a significant step forward in Java’s garbage collection technology, offering improved throughput, reduced pause times, and enhanced overall application performance. Its design reflects a deep understanding of application memory management needs. As Java applications continue to grow in complexity and scale, the adoption of Generational ZGC could be a pivotal factor in achieving the performance goals of modern, high-demand applications.
Conclusion
The transition from Java 17 to Java 21 heralds a new era of Java development, characterized by significant improvements in performance, security, and developer-friendly features. The API changes and enhancements discussed above are just the tip of the iceberg, with Java 21 offering a wealth of other features and improvements designed to cater to the evolving needs of modern application development. As developers, embracing Java 21 and leveraging its new features and improvements can significantly impact the efficiency, performance, and security of Java applications. Whether it’s through the enhanced I/O capabilities, improved serialization exception handling, or the new Unicode support in the `Character` class, Java 21 offers a compelling upgrade path from Java 17, promising to enhance the Java ecosystem for years to come. In conclusion, the evolution from Java 17 to Java 21 is a testament to the ongoing commitment to advancing Java as a language and platform. By exploring and adopting these new features, developers can ensure their Java applications remain cutting-edge, secure, and performant in the face of future challenges.