JDK 22 was released on 19 March 2024. This post provides a quick look at all the features we are excited about in Java 22.
Java’s previous version JDK 21 (LTS) was released on 19 September 2023 and brought 15 new features including record patterns, virtual threads, string templates (preview phase), and more.
âšī¸ Updates in other Java releases
Check out our posts that describe the new and updated features in other JDK versions:
Java 22 continues along those lines, and brings some new updates for developers. Read on for detailed descriptions (some with examples) of the 12 new features in Java 22.
Table of contents:
- What to expect in Java 22?
- Java 22 features explained
- 423: Region Pinning for G1
- 447: Statements before super(…) (Preview)
- 454: Foreign Function & Memory API
- 456: Unnamed Variables & Patterns
- 457: Class-File API (Preview)
- 458: Launch Multi-File Source-Code Programs
- 459: String Templates (Second Preview)
- 460: Vector API (Seventh Incubator)
- 461: Stream Gatherers (Preview)
- 462: Structured Concurrency (Second Preview)
- 463: Implicitly Declared Classes and Instance Main Methods (Second Preview)
- 464: Scoped Values (Second Preview)
- Generating tests for Java applications with Symflower
What to expect in Java 22?
The feature set for JDK 22 is frozen, meaning that no new features or JEPs (JDK Enhancement Proposals) will be added. To get a sneak peek, access the early access builds of JDK 22 for Linux, MacOS, and Windows at https://jdk.java.net/22/.
đ¤ˇââī¸ Not familiar with the JDK process?
Read our thorough explanation of the JDK Enhancement Proposal (JEP) & roadmap process in our blog post about the updates in Java 20!
Here’s a list of all the features planned for Java 22:
Core language extensions and updates you should definitely check out
Java 22 continues on the path set out by JDK 21, with multiple core language updates.
Unnamed variables & patterns greatly improve code clarity and maintainability. String templates, which will get their second preview in JDK 22, enable developers to produce specialized non-string results computed at runtime.
Support for statements before super(...)
enables more flexibility in how you use constructors and provides a more natural way for argument validation, preparation, and sharing. The new stream gatherer feature improves the flexibility and expressiveness of Stream pipelines by enabling custom intermediate data transform operations.
With the second preview of implicitly declared classes and instance main methods, it becomes easier to write simple programs. And finally, the Java application launcher can now run applications in multiple source files.
- 456: Unnamed Variables & Patterns
- 459: String Templates (Second Preview)
- 447: Statements before super(…) (Preview)
- 461: Stream Gatherers (Preview)
- 463: Implicitly Declared Classes and Instance Main Methods (Second Preview)
- 458: Launch Multi-File Source-Code Programs
Extensions to multithreaded code
Supporting multithreaded code has been a core part of Java 21, and remains a focus area in JDK 22.
Java 22 brings a second preview of structured concurrency, a feature that simplifies multithreaded programming by enabling developers to handle related tasks running in separate threads as one unit of work. This helps better coordinate virtual threads and provides transparency by enabling observability tools to display threads in a way that’s easy to understand for the developer.
Scoped values will also get their second preview in JDK 22. This feature lets developers share immutable data across threads without the use of method arguments.
Performance and infrastructure improvements
Naturally, JDK 22 also brings a variety of performance and infrastructure enhancements to the Java language.
Region pinning in the default G1 garbage collector (GC) contributes to lower latency and makes sure that GC no longer has to be disabled in JNI critical regions. The new foreign function & memory API (finalized after 3 previews in previous versions) provides better interoperability with code and data outside the Java runtime.
Reaching its 7th incubator stage, Vector API helps improve Java’s object model by enabling the expression of vector computations compiled at runtime. A new preview feature, Class-file API, helps parse, generate, and transform Java class-files to avoid compatibility issues across the various available libraries.
- 423: Region Pinning for G1
- 454: Foreign Function & Memory API
- 460: Vector API (Seventh Incubator)
- 457: Class-File API (Preview)
Java 22 features explained
423: Region Pinning for G1
The implementation of region pinning in G1 helps reduce latency and ensure that garbage collection doesn’t need to be disabled during JNI (Java Native Interface) critical regions.
When developing an application that requires interoperation with unmanaged programming languages (the likes of C and C++), JNI uses functions to 1) get and then 2) release direct pointers to Java objects. These function pairs run in a critical region and they mustn’t be moved during garbage collection. You can either pin them to their locations or just disable garbage collection altogether as long as a thread is in a critical region. This latter approach (which the default garbage collector G1 uses) can be problematic because the garbage collector has to wait until no thread is in a critical region.
But stalling threads is a thing of the past: with region pinning being introduced in JDK 22, Java threads will no longer have to wait for a G1 garbage collection operation to finish. This reduces latency and regressions in garbage collection.
447: Statements before `super(...)` (Preview)
This new (preview) feature provides more flexibility in the use of constructors by allowing statements that don’t have references to the instance being created to precede an explicit constructor invocation.
In case your code includes a class that extends another class, the subclass will inherit the functionality of the superclass. New functionality can be added by declaring new fields and methods. But because of potential dependencies, you’ll have to make sure that the fields of the superclass are initialized before the fields of the subclass itself.
In previous Java versions super(...)
could not be preceded by any other statements in a constructor. By supporting statements before super(...)
, this new (preview) feature provides a more natural way for argument validation, argument preparation, and argument sharing in constructors. With the new solution, constructors are still run in top-down order during class instantiation, but it provides developers with more flexibility to express constructor behavior and logic.
454: Foreign Function & Memory API
Foreign function & memory API already had three previews in previous versions: JDK 19, 20, and 21. With Java 22, it’s becoming a final feature.
As we described in our post about JDK 21, the goal of this new feature is to provide interoperability with code and data outside of the Java runtime without using the Java Native Interface (JNI). This new API enables developers to invoke foreign functions (code external to the JVM) and to safely access memory not managed by the JVM.
JNI is considered a brittle and unsafe way to call native libraries and process native data. As a superior solution in terms of performance, integrity, and broad platform support, the new Foreign function & memory API is intended to replace JNI altogether. It offers a sound and uniform way to operate on structured or unstructured data in a variety of memory types (e.g. native, persistent, and managed heap memory).
456: Unnamed Variables & Patterns
Another feature that has gone through a preview phase in Java 21, unnamed variables & patterns has matured into a final feature in JDK 22.
Denoted by the underscore character ('_'), unnamed patterns and variables improve code clarity and maintainability, and reduce the chance of error. Unnamed variables and patterns are common to many languages including Scala and Python â from JDK 22 on, they will also be available in Java. What exactly do they mean? Here’s a quick explanation:
- Unnamed variables are variables that may be initialized but are not used.
- Unnamed patterns match a record component without stating the component’s name or type.
By introducing unnamed variables and unnamed patterns, this new feature helps better capture developer intent for unused parameters (e.g variables that need to be declared but will not be used) and removes the need for using nested type patterns. Using it helps reduce static source code analysis warnings such as “return argument not used” or “variable declared but not used” in case they were made as intentional choices from the developer.
Unnamed variables can be used in the following cases:
- A local variable declaration statement in a block
- The resource specification of a try-with-resources statement
- The header of a basic for loop
- The header of an enhanced for loop
- An exception parameter of a catch block
- A formal parameter of a lambda expression
Source: openjdk.org
Take a look at the following example taken from the JEP:
static int count(Iterable<Order> orders) {
int total = 0;
for (Order order : orders) // order is unused
total++;
return total;
}
The local variable order has to be declared to iterate over the elements of orders
, but is irrelevant to the body of the loop. By using an unnamed variable, the above code can be simplified to:
static int count(Iterable<Order> orders) {
int total = 0;
for (Order _ : orders) // Unnamed variable
total++;
return total;
}
In addition to unnamed variables, unnamed patterns can also be used. Take a look at the following example:
static void printColorOfUpperLeftPoint(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(Point p, Color c),
ColoredPoint lr)) {
System.out.println(c);
}
}
With unnamed patterns, we can state explicitly that Point p
and ColoredPoint lr
are irrelevant for the preceding code. This helps simplify the above code snippet to:
static void printColorOfUpperLeftPoint(Rectangle r) {
if (r instanceof Rectangle(ColoredPoint(_, Color c),_) {
System.out.println(c);
}
}
457: Class-File API (Preview)
Another new (preview) feature in Java 22 is a standard API for parsing, generating, and transforming Java class-files.
Class-files are important because they allow developers of third-party tools and libraries to add functionality while making sure their source code is easy to maintain. Java offers a variety of libraries for parsing and generating class-files so developers can find the one that works best for their use case. However, the fast evolution of class-file formats poses a compatibility challenge.
With the new API, it’s possible to process class-files while tracking the class-file format that is defined by the Java Virtual Machine Specification. The API evolves alongside the class-file format, making sure that the latest JDK’s class files are supported. This enables developers to use the latest language and VM features without having to worry about compatibility.
458: Launch Multi-File Source-Code Programs
This new feature enables the Java application launcher to run programs that come packaged in multiple source code files. The aim is to make the transition from small to large programs easier, allowing developers to put off the task of having to configure a build tool for as long as they see fit.
Ever since JDK 11, the Java application launcher has been able to run .java files directly without having to go through compilation. Let’s assume, for instance, that you have a main method in the file App.java
. You can then run this application simply using java App.java
.
Up until JDK 22, this solution has been limited to single-file applications. If your application is supplied in more than one Java source file, you currently have to go back to compiling source files explicitly. That changes with this new feature in Java 22, giving developers the freedom to experiment and hack away without having to compile the program explicitly: just edit, run, and repeat.
The Java application launcher will compile all classes that are referenced (directly or indirectly) in App.java
and that reside in the same directory. Classes that are not referenced but reside in the same directory will not be automatically compiled. This allows for easier transition from small to large programs, letting developers choose whether (and when) to configure a build tool.
459: String Templates (Second Preview)
If you’ve read our report on the previous Java version, you know that string templates already had their first preview phase in JDK 21. In Java 22, they will make it into the second preview (the only difference since the first one is a technical change in the types of template expressions).
String templates enable developers to combine literal text with embedded expressions and template processors. Together with string literals and text blocks (already part of Java), this new feature lets developers produce specialized non-string results that are computed at runtime.
This new feature improves the readability of such mixed expressions, enhances security, and provides flexibility around using APIs that accept strings in non-Java languages like SQL, XML, or JSON.
Here’s a simple example of a string template:
String name = "Joan";
String info = STR."My name is \{name}";
assert info.equals("My name is Joan"); // true
In this example, STR
is a template processor that performs string interpolations and that is automatically statically imported to any Java class. It is able to evaluate template expressions like STR."My name is \{name}"
. Dedicated template processors like FMT
(for interpreting format specifiers) are available for use. Check out the JEP to find out how to specify user-defined template processors.
460: Vector API (Seventh Incubator)
Vector API is nothing new either. It reached the 6th incubator phase in JDK 21, and has only undergone minor enhancements (bug fixes and performance improvements) for its 7th incubator in Java 22. The only notable change compared to the 6th incubator in JDK 21 is that the API now supports vector access with heap MemorySegments backed by an array of any primitive element type (as opposed to an array of byte, like previously).
The new API enables the expression of vector computations that compile at runtime to optimal vector instructions (on the CPU architectures that are supported e.g. x64 and AArch64 architectures). Vector API can thus contribute to performance improvements while enabling the clear expression of a wide range of vector computations.
As part of Project Valhalla, Vector API is expected to remain in incubation over several releases. Project Valhalla aims to enhance Java’s object model, which will mean that Vector API’s current value-based classes will be changed to value classes to enable applications to work with value objects (class instances without object identity).
461: Stream Gatherers (Preview)
The Stream API in Java allows to process streams of data with a theoretically infinite number of values. A stream pipeline contains three types of elements:
- A stream of elements
- Any number of intermediate operations
- A closing operation
Take a look at the following example from the JEP:
long numberOfWords =
Stream.of("the", "", "fox", "jumps", "over", "the", "", "dog") // (1)
.filter(Predicate.not(String::isEmpty)) // (2)
.collect(Collectors.counting()); // (3)
The above expression computes the number of words within the supplied stream.
- (1) shows the definition of the stream
- (2) an intermediate operation and
- (3) a closing operation.
Up until now, several predefined intermediate operations were available, but there was no option to define custom ones. Stream gatherers enable the Stream API to support custom intermediate operations to improve the flexibility and expressiveness of Stream pipelines.
This Stream Gatherers preview feature enhances Stream pipelines, enabling them to perform data transform operations that surpass the limitations of current built-in intermediate operations. Stream Gatherers will allow custom intermediate operations to handle streams of infinite size.
462: Structured Concurrency (Second Preview)
Yet another feature that has been previewed in JDK 21, Structured Concurrency aims to simplify multithreaded programming. This new feature enables developers to handle related tasks running in separate threads as one unit of work. This has a range of benefits including better error handling and cancellation, enhanced reliability, and more transparency when observing these tasks.
JDK developers hope that this feature will encourage developers to adopt a style of concurrent programming that helps reduce the common risks associated with cancellation and shutdown (think about problems like thread leaks and cancellation delays).
463: Implicitly Declared Classes and Instance Main Methods (Second Preview)
While this feature has been renamed, you may know it from Java 21 as 445: Unnamed Classes and Instance Main Methods (preview). The primary aim of this feature is to help new Java users (e.g. students) write programs before thoroughly understanding all the language features that are intended to be used with large programs like classes and modifiers.
With implicitly declared classes and instance main methods, students can write streamlined declarations for simple, single-class programs, and educators can gradually introduce more advanced concepts in the Java language.
Without this feature, now in second preview state, the typical HelloWorld program in Java looks like this:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
With the feature, it is no longer necessary to use String[]
args as a parameter, simplifying the code snippet to:
void main() {
System.out.println("Hello, World!");
}
464: Scoped Values (Second Preview)
Finally, there’s the new feature of scoped values, already previewed in JDK 21. Scoped values let developers share immutable data (with child frames in the same thread and with child threads) within and across threads, without the use of method arguments.
Scoped values form a powerful combination with Virtual Threads and Structured Concurrency, offering lower space and time costs. The primary goal of introducing this new feature is to facilitate reasoning about the data flow while also improving robustness, performance, and comprehensibility.
Generating tests for Java applications with Symflower
Whichever Java version you’re using, you can use Symflower to generate code for your applications. Symflower’s test templates and complete test suites can drastically reduce the time it takes to create tests:
We also provide test impact analysis functionality to reduce test execution times by 29% on average by automatically running affected tests only, and can support you finding and optimizing LLMs to improve quality and accelerate software development.
đ¤ Check out these Java resources
- The best IntelliJ IDEA productivity plugins for Java developers
- What are the top Java unit testing frameworks & tools in 2024?
- What are Java modules and how to use them?
- The best static analysis tools and linters for Java
- How to write reusable code? Guide & best practices for reusability in Java
- Mocking frameworks for Java: Mockito vs EasyMock vs JMockit
- Java Unit Testing Frameworks Compared: JUnit vs TestNG differences and similarities
Make sure you never miss any of our upcoming content by signing up for our newsletter and by following us on X, LinkedIn, or Facebook!