JDK 22: New Features of Java 22
Last Updated :
24 Oct, 2024
JDK 22 release has been much awaited by the Java landscape which continues to witness modifications. The latest installation of JDK has more than enough technical improvements aimed at giving developers more control, simplifying development processes, and developing brand-new paradigms for high-performance Java application creation.

With this article, we want to introduce the developers, to the world of JDK 22. For each feature, we will be discussing in detail with code snippets and explanations that will help one understand them better. Basically, you will be ready to apply these innovations in your further growth as a Java developer.
JDK 22: A Brief Overview
JDK 22, released in March 2024, marks a significant step forward for Java. It introduces several preview features alongside improvements to existing libraries and the garbage collector. These enhancements aim to streamline development processes, empower developers with greater control, and boost Java's overall performance.
Release Date and Support Details
JDK 22 reached General Availability (GA) on March 19, 2024. Oracle provides production-ready binaries under the GPL(General Public License), with binaries from other vendors expected to follow shortly.
The Significance of JDK 22 in the Java Ecosystem
The Java ecosystem thrives on continuous innovation. JDK 22 plays a crucial role by:
- Enhancing Developer Productivity: Features like scoped values and stream gatherers simplify thread handling and stream processing, respectively.
- Improving Code Readability: Unnamed variables and patterns contribute to cleaner and more maintainable code.
- Boosting Performance: Region pinning for the G1 garbage collector optimizes memory management, leading to faster applications.
- Simplifying Concurrent Programming: Structured concurrency offers a more intuitive approach to managing concurrent tasks.
JDK 22: New Features of Java 22
1. Scoped Values
Scoped values introduce a novel approach to managing shared data within a thread or across child threads. They offer several advantages over traditional thread-local variables:
- Immutability: Scoped values are immutable, preventing accidental modification and improving thread safety. This eliminates the need for complex synchronization mechanisms often required with thread-local variables.
- Automatic Lifecycle Management: Scoped values are automatically cleaned up when they go out of scope (typically when the thread or enclosing block exits). This simplifies memory management and reduces the risk of memory leaks associated with thread-local variables.
- Thread-Safe Sharing: Scoped values enable safe sharing of data between parent and child threads while maintaining immutability. A parent thread can create a child thread with a specific scoped value, ensuring the child thread has access to the data without modifying the original value.
Imagine each customer (thread) in a store has a shopping cart (scoped value) to hold their items. This cart is specific to the customer and gets discarded when they leave (the thread exits). This emphasizes both immutability (the items in the cart can't be changed) and automatic cleanup (the cart disappears after use).
Example: Tracking Current User
Here's a practical example demonstrating scoped values using a simple code snippet, aligned with the syntax introduced in JDK 22:
Java
public class AuthService {
private static final ScopedValue<String> USER_ID = ScopedValue.newInstance(); // Initialize ScopedValue outside the method
public String getWelcomeMessage(String userId) {
try (var scope = ScopedValue.where(USER_ID, userId) // Use where instead of of
.run(() -> { // Use run instead of constructor
String username = getUserDetails(userId).getName();
return "Welcome, " + username + "!";
})) { // User ID is discarded after the message is generated
}
}
}
- We create a scoped value instance (
USER_ID
) outside the method using newInstance
to avoid unnecessary object creation on each call. - Inside the
getWelcomeMessage
method, we use ScopedValue.where(USER_ID, userId
)
to define a scope with the user ID as the key. - The
run
method takes a runnable that retrieves the username and generates the welcome message. This code executes within the scope defined earlier.
In this example, the user ID is only accessible within the scope of generating the welcome message, simplifying data management and reducing memory leaks.
2. Stream Gatherers
Stream gatherers provide a more efficient way to collect results from streams. They allow developers to define custom logic for accumulating data during stream processing. Unlike traditional terminal operations that return a single value, stream gatherers offer greater flexibility by enabling developers to accumulate results in various ways. For instance, a stream gatherer could be used to collect data into a specific data structure or perform custom calculations on the stream elements during the accumulation process.
3. Structured Concurrency
Structured concurrency aims to simplify the management of asynchronous operations by treating related tasks as a single unit of work. This approach enhances readability, error handling, and cancellation management.
Benefits:
- Improved Readability: Clearly defines the start, execution, and completion of concurrent tasks within a structured block.
- Streamlined Error Handling: Propagates errors through the entire unit of work, simplifying error management.
- Enhanced Cancellation: Ensures all tasks within the group are terminated properly when cancellation is requested.
Here's an example of how structured concurrency can be used to download multiple files concurrently:
Using traditional concurrency:
Java
public void downloadFiles(List<String> fileUrls) {
List<Thread> threads = new ArrayList<>();
for (String url : fileUrls) {
Thread thread = new Thread(() -> downloadFile(url));
threads.add(thread);
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
This code can be refactored using structured concurrency:
Java
import java.util.concurrent.*;
public void downloadFiles(List<String> fileUrls) throws InterruptedException, ExecutionException {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<?>> futures = new ArrayList<>();
for (String url : fileUrls) {
futures.add(executor.submit(() -> downloadFile(url)));
}
for (Future<?> future : futures) {
future.get(); // Waits for all tasks to complete
}
}
}
4. Statements Before Super
Prior to JDK 22, code execution within a constructor had to occur after the super call, which initializes the parent class. This restriction often forced developers to place initialization logic within the parent class constructor or use instance initialization blocks, which could lead to code duplication or awkward workarounds. Statements Before Super relaxes this restriction, allowing developers to place essential initialization logic before invoking the superclass constructor. This enhances flexibility and control over object initialization by enabling developers to perform necessary setup tasks before delegating to the parent class constructor.
Consider a class Rectangle
that inherits from a base class Shape
with a constructor that sets the color:
Java
public class Shape {
private String color;
public Shape(String color) {
this.color = color;
}
}
public class Rectangle extends Shape {
private int width;
private int height;
// Prior to JDK 22, initialization logic had to go here
public Rectangle(int width, int height, String color) {
super(color); // Call to superclass constructor
this.width = width;
this.height = height;
}
}
With statements before super, the initialization logic for width
and height
can be placed before the superclass constructor call:
Java
public class Rectangle extends Shape {
private int width;
private int height;
public Rectangle(int width, int height, String color) {
this.width = width;
this.height = height;
super(color); // Call to superclass constructor after setting width and height
}
}
5. Class-File API
The Class-File API provides programmatic access to Java class files. This allows developers to inspect, modify, and generate class files at runtime. Here are some potential use cases for the Class-File API:
- Bytecode Manipulation: Developers can write applications that modify the bytecode of existing classes at runtime. This can be used for techniques like code instrumentation, where additional code is inserted into a class to monitor behavior or collect performance data.
- Custom Class Loaders: The Class-File API can be leveraged to build custom class loaders that dynamically generate or modify classes at load time. This can be beneficial for applications that require custom class loading behavior or need to adapt class definitions based on runtime conditions.
- Metaprogramming: The ability to inspect and modify class files opens doors for advanced metaprogramming techniques. Developers can potentially write programs that generate code based on annotations or other metadata within class files.
6. Region Pinning for the G1 Garbage Collector (G1 GC)
Region pinning is a performance optimization technique for the G1 garbage collector. The G1 GC divides the heap memory into smaller regions. During garbage collection cycles, the G1 GC identifies and collects regions with a high concentration of dead objects, improving memory efficiency. Region pinning allows developers to designate specific memory regions as pinned, preventing them from being moved during garbage collection cycles. This is particularly beneficial for frequently accessed data, especially in applications with large memory footprints. Pinning these regions ensures the data remains readily available in its current location, potentially reducing memory access times and improving application performance.
7. String Templates (Second Preview)
String templates (still in preview) offer a convenient way to construct complex strings. They combine literal text with embedded expressions, enabling developers to create dynamic strings with improved readability compared to traditional string concatenation. Here's how string templates work:
- Template Literals: String templates utilize special delimiters (typically ${}) to mark embedded expressions within the string.
- Expression Evaluation: During string template processing, the Java runtime engine evaluates the embedded expressions and incorporates the results into the final string.
- Improved Readability: String templates enhance readability by clearly separating literal text from expressions. This can be particularly beneficial for complex strings that involve multiple concatenations and logic evaluations.
Consider the following example of constructing a greeting message using traditional string concatenation:
Java
String name = "Ayaan";
int age = 23;
String greeting = "Hello, my name is " + name + " and I am " + age + " years old.";
This approach can become cumbersome for more complex scenarios. With string templates, the greeting message can be written more concisely and readable:
Java
String name = "Ayaan";
int age = 23;
String greeting = "Hello, my name is ${name} and I am ${age} years old.";
8. Unnamed Variables and Patterns
Unnamed variables and patterns are syntactic sugar that enhance code readability. They allow developers to omit variable names when the value is not used or when patterns are employed for matching purposes only. This can be particularly beneficial in scenarios with:
- Complex Conditional Statements: Unnamed variables can be used within conditional expressions where the resulting value is not explicitly assigned to a variable but rather used for comparison or logical operations.
- Destructuring Assignments: When destructuring assignments involve elements that are not required for further processing, unnamed patterns can be used to discard those elements, improving code conciseness.
This article has explored some of the major features and changes introduced in JDK 22. These features aim to streamline development processes, enhance code readability, and boost overall Java performance.
To explore the complete list of features and changes in JDK 22, refer to the official documentation:
JDK 22 Release Notes: https://www.oracle.com/java/technologies/javase/22all-relnotes.html
Impact on Java Development
JDK 22 significantly impacts Java development by:
- Reducing Boilerplate Code: Features like stream gatherers and scoped values eliminate the need for manual code to handle common tasks associated with stream processing and thread-local data management.
- Improving Code Maintainability: Unnamed variables and patterns contribute to cleaner and more concise code, making it easier to understand and modify for both the developer who wrote it and future maintainers.
- Enhancing Developer Productivity: The new features empower developers to write more efficient and robust code, leading to faster development cycles by reducing the time spent on complex tasks and error handling.
- Unlocking New Possibilities: The Class-File API opens doors for advanced development techniques like bytecode manipulation and custom class loading, enabling developers to create highly specialized applications.
By making common development tasks easier to do, JDK 22 improves code readability and brings in powerful new capabilities that make it possible to create faster, stronger and more enjoyable Java programs.
Comparison with Previous Versions
JDK 22 builds upon its predecessors in several ways:
- Focus on Developer Experience: Compared to previous releases, JDK 22 introduces a wider range of preview features aimed at improving developer experience. Features like scoped values, stream gatherers, and statements before super address common pain points and provide developers with more control and flexibility.
- Enhanced Concurrency Support: Structured concurrency represents a significant step forward in Java's approach to handling concurrent tasks. This new paradigm offers a more intuitive and manageable way to reason about asynchronous operations compared to traditional callback-based approaches found in previous versions.
- Performance Optimization: Region pinning for G1 GC shows how Java constantly works towards maintaining high performance through targeted optimizations. Developers can adjust memory management in this way to suit specific use cases which may result in better performance when dealing with applications having larger memory footprints.
- Exploration of New Paradigms: The inclusion of preview features like the Class-File API suggests Java's willingness to explore new possibilities and integrate innovative approaches. This openness to exploration can pave the way for future advancements in the language.
Future of Java
The direction of Java development gleaned from JDK 22 points towards:
- Growing focus on developer experience: The stress on functionality that simplifies complicated procedures and enhances code legibility implies the intention of making Java a more user-friendly language. We should expect further improvements in such areas as tooling, code analysis and debugging support.
- Enhanced Concurrency Support: Structured concurrency is likely to play a more prominent role in future Java development. We can expect to see further refinements and potentially additional features that solidify this paradigm as the preferred approach for managing concurrent tasks.
- Performance Optimization: Continued efforts to optimize the garbage collector and other core components will likely remain a priority. Additionally, future releases may introduce features that enable developers to profile and optimize applications more effectively.
- Exploration of New Paradigms: Java is likely to continue exploring new language features and paradigms that address emerging development challenges. The Class-File API is an example of this exploration, and future releases may introduce features inspired by functional programming or reactive programming concepts.
Conclusion
JDK 22 represents a major milestone for Java development. These new features deliver big gains for Java programmers; from better developer productivity to improved performance. Focusing on developer experience, concurrency support and performance optimization, JDK 22 makes way for a more efficient, robust and enjoyable Java development experience. As the evolution of Java continues, developers will be able to look forward to more advancements that empower them to develop outstanding applications.
Similar Reads
Java Tutorial
Java is a high-level, object-oriented programming language used to build web apps, mobile applications, and enterprise software systems. It is known for its Write Once, Run Anywhere capability, which means code written in Java can run on any device that supports the Java Virtual Machine (JVM).Java s
10 min read
GeeksforGeeks Practice - Leading Online Coding Platform
GeeksforGeeks Practice is an online coding platform designed to help developers and students practice coding online and sharpen their programming skills with the following features. GfG 160: This consists of 160 most popular interview problems organized topic wise and difficulty with with well writt
6 min read
Java OOP(Object Oriented Programming) Concepts
Java Object-Oriented Programming (OOPs) is a fundamental concept in Java that every developer must understand. It allows developers to structure code using classes and objects, making it more modular, reusable, and scalable.The core idea of OOPs is to bind data and the functions that operate on it,
13 min read
Java Interview Questions and Answers
Java is one of the most popular programming languages in the world, known for its versatility, portability, and wide range of applications. Java is the most used language in top companies such as Uber, Airbnb, Google, Netflix, Instagram, Spotify, Amazon, and many more because of its features and per
15+ min read
Arrays in Java
Arrays in Java are one of the most fundamental data structures that allow us to store multiple values of the same type in a single variable. They are useful for storing and managing collections of data. Arrays in Java are objects, which makes them work differently from arrays in C/C++ in terms of me
15+ min read
Inheritance in Java
Java Inheritance is a fundamental concept in OOP(Object-Oriented Programming). It is the mechanism in Java by which one class is allowed to inherit the features(fields and methods) of another class. In Java, Inheritance means creating new classes based on existing ones. A class that inherits from an
13 min read
7 Different Ways to Take a Screenshot in Windows 10
Quick Preview to Take Screenshot on Windows 10:-Use the CTRL + PRT SC Keys to take a quick screenshot.Use ALT + PRT SC Keys to take a Screenshot of any application window.Use Windows + Shift + S Keys to access the Xbox Game Bar.Use Snip & Sketch Application as well to take screenshotTaking Scree
7 min read
Collections in Java
Any group of individual objects that are represented as a single unit is known as a Java Collection of Objects. In Java, a separate framework named the "Collection Framework" has been defined in JDK 1.2 which holds all the Java Collection Classes and Interface in it. In Java, the Collection interfac
15+ min read
Java Exception Handling
Exception handling in Java allows developers to manage runtime errors effectively by using mechanisms like try-catch block, finally block, throwing Exceptions, Custom Exception handling, etc. An Exception is an unwanted or unexpected event that occurs during the execution of a program, i.e., at runt
10 min read
Java Interface
An Interface in Java programming language is defined as an abstract type used to specify the behaviour of a class. An interface in Java is a blueprint of a behaviour. A Java interface contains static constants and abstract methods. Key Properties of Interface:The interface in Java is a mechanism to
12 min read