We provide an overview and details about the features delivered in Java 23 (JDK 23) released on 17 September 2024. Read the post to learn about what is new in Java 23 and why these new and updated features are useful.
π Easier Java code testing
Use Symflower to generate test code for your applications. Smart test templates generated by Symflower provide the necessary imports, annotations, object initializations, function calls, and asserts, making them ideal boilerplates. Symflower’s test suites are ready for test execution as they also contain input values. Here’s an example of how Symflower works:
What is new in Java 23? A summary of udpates in JDK 23
The OpenJDK website for Java 23 lists the JEPs for all of the 12 features in this release. Browse that site for exhaustive information on the background of each update, and read on for short descriptions and examples of the updates in JDK 23.
π€ How do JDK Enhancement Proposals work?
Read our introduction of the JDK Enhancement Proposal (JEP) & roadmap process.
Table of contents:
- What is new in Java 23? A summary of udpates in JDK 23
- 455: Primitive Types in Patterns, instanceof, and switch (Preview)
- 466: Class-File API (Second Preview)
- 467: Markdown Documentation Comments
- 469: Vector API (Eighth Incubator)
- 473: Stream Gatherers (Second Preview)
- 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal
- 474: ZGC: Generational Mode by Default
- 476: Module Import Declarations (Preview)
- 477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
- 480: Structured Concurrency (Third Preview)
- 481: Scoped Values (Third Preview)
- 482: Flexible Constructor Bodies (Second Preview)
- JDK 23 changes and features
455: Primitive Types in Patterns, instanceof, and switch (Preview)
This preview feature is intended to enhance pattern matching and enable uniform data exploration. It allows primitive type patterns in all pattern contexts (e.g. in primitive and reference types and both nested and top-level contexts). It also extends instanceof
and switch
to work with all primitive types.
Here’s what a switch case would look like without primitive type patterns:
switch (x.getStatus()) {
case 0 -> "okay";
case 1 -> "warning";
case 2 -> "error";
default -> "unknown status: " + x.getStatus();
}
With this preview feature in JDK 23, the above code can be adapted to:
switch (x.getStatus()) {
case 0 -> "okay";
case 1 -> "warning";
case 2 -> "error";
case int i -> "unknown status: " + i;
}
466: Class-File API (Second Preview)
In preview in JDK 22, Class-File API gets its second preview in this release.
This update adds a standard API for parsing, generating, and transforming Java class-files. The Class-File API tracks the class file format defined by JVM and enables JDK components to migrate to the standard API. The eventual goal is to remove the JDK’s internal copy of the third-party ASM library.
There are a few changes in JDK 23 compared to the first preview phase in JDK 22, most notably:
- The
CodeBuilder
class has been streamlined, with duplicate or infrequently used methods removed and some methods renamed. - The
ClassSignature
class has also been updated to increase the accuracy of modeling the generic signatures of superclasses and superinterfaces.
467: Markdown Documentation Comments
Java’s developers wanted to simplify writing and reading API documentation comments in source form. After this update, you will be able to use markdown syntax in documentation comments (as well as HTML elements and JavaDoc tags).
The update also extends the Compiler Tree API to ensure other tools can adequately handle the Markdown content in comments. This should make it more convenient for developers to manage comments in the documentation of their applications.
469: Vector API (Eighth Incubator)
Part of Project Valhalla, Vector API reaches its eighth incubator stage in JDK 23.
We introduced this feature in our post that provides descriptions for the updates in JDK 21. As a quick reminder, this update introduces an API to express vector computations that compile at runtime to optimal vector instructions. The Vector API enables a wide range of vector computations to be clearly expressed and is expected to result in performance improvements.
π€ Curious about Vector API & other JDK 21 features?
Read our earlier post about Java 21 with descriptions of Vector API and all the other features in that release!
Supported CPU architectures include x64
and AArch64
architectures. Developers of JDK proposed to re-incubate the feature this time without any significant changes compared to JDK 22.
Here is a simple scalar computation over elements of arrays:
void scalarComputation(float[] a, float[] b, float[] c) {
for (int i = 0; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
Here is an equivalent vector computation using the Vector API:
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) {
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
// FloatVector va, vb, vc;
var va = FloatVector.fromArray(SPECIES, a, i);
var vb = FloatVector.fromArray(SPECIES, b, i);
var vc = va.mul(va)
.add(vb.mul(vb))
.neg();
vc.intoArray(c, i);
}
for (; i < a.length; i++) {
c[i] = (a[i] * a[i] + b[i] * b[i]) * -1.0f;
}
}
473: Stream Gatherers (Second Preview)
Similarly, Stream Gatherers haven’t received any changes either: this feature is re-previewed without further changes in JDK 23 to gain more feedback.
A quick memo about Stream Gatherers: this feature enables the Stream API (JDK’s API for processing data streams) to support custom intermediate operations. These help you transform data in ways that the currently available built-in intermediate operations don’t allow. This update improves the flexibility and expressiveness of Stream pipelines.
π Get the low-down on Stream Gatherers
Check out our post about Java 22 which offres a detailed description of Stream Gatherers.
471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal
The memory-access methods in sun.misc
are now deprecated (but were deemed unsafe for removal in upcoming releases). These methods are no longer needed as they were superseded by two standard APIs: the VarHandle API and the Foreign Functions & Memory API.
Developers are encouraged to migrate from sun.misc.Unsafe
to the above-mentioned replacements.
474: ZGC: Generational Mode by Default
The ZGC (Z Garbage Collector) is switched to generational mode as the non-generational mode is waiting for removal in an upcoming release. That’s because maintaining non-generational ZGC would have slowed down the development of new features.
A quick reminder: generational ZGC can collect young objects more frequently as they are likely to die sooner, and is deemed a better solution for most use cases than other garbage collectors.
βοΈ Learn about ZGC
Read our detailed description of the Z Garbage Collector that was finalized in JDK 21.
476: Module Import Declarations (Preview)
To make it easier to reuse modular libraries, this feature introduces declarations that help you simply import all the packages exported by a module. The feature also helps beginners new to the Java language since they don’t have to memorize where third-party libraries or fundamental Java classes are located.
Example for module import declarations:A module import declaration would look as follows:
import module M;
This imports all the public top-level classes and interfaces in:
- The packages that module M exports to the current module.
- All the packages that the modules being read by the current module (due to reading the module M) export.
477: Implicitly Declared Classes and Instance Main Methods (Third Preview)
Another feature that beginners can benefit from is Implicitly Declared Classes and Instance Main Methods. This update makes it easier to write declarations for simple, single-class programs.
See the example from our post about Java 22.
Without implicitly declared classes and instance main methods, the classic HelloWorld
would look like this:
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
This feature lets students skip the use of String[] args
as a parameter. So the code snippet is simplified to:
void main() {
System.out.println("Hello, World!");
}
480: Structured Concurrency (Third Preview)
Structured concurrency is reaching its third preview stage. The ultimate goal of this new feature is to simplify concurrent programming, promoting a way that lets developers cut the risks associated with cancellation and shutdown (like thread leaks or cancellation delays).
Simply put, structured concurrency lets you as a developer manage related tasks that run in separate threads as one unit of work. In this version, the API is re-previewed without any changes.
π€ What is structured concurrency?
Read our description of structured concurrency in our post introducing the features of Java 22.
The method handle()
represents a task in a server application. It handles an incoming request by submitting two subtasks:
Response handle() throws ExecutionException, InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<String> user = scope.fork(() -> findUser());
Supplier<Integer> order = scope.fork(() -> fetchOrder());
scope.join() // Join both subtasks
.throwIfFailed(); // ... and propagate errors
// Here, both subtasks have succeeded, so compose their results
return new Response(user.get(), order.get());
}
}
Using StructuredTaskScope
brings a few benefits:
- Clear structure with subtasks either completed or being cancelled.
- Easier error handling with short-circuiting: in case either
findUser()
orfetchOrder()
fails, the other subtask will be canceled. - Cancellation propagation e.g. if the thread running
handle()
is interrupted before (or during) the call tojoin()
is made, both subtasks will be automatically canceled.
481: Scoped Values (Third Preview)
This feature was previewed in Java 21 and is currently in second preview phase in JDK 22.
With this new feature, you can share immutable data both within threads and with child threads - all without using method arguments. Scoped values are geared towards robustness, performance, and comprehensibility. They have lower space and time costs and you can use them in combination with virtual threads and structured concurrency.
π‘On scoped valuesβ¦
Read our description of scoped values in our post about Java 22.
With scoped values, it’s easier to reason about data flow. If you’re using a large number of virtual threads, scoped values come in handy and should be preferred over thread-local variables. That’s because thread-local variables are mutable, have an unbounded lifetime, and need to be allocated in inheriting child threads in all cases, so there are a few downsides to their use.
There’s only one change vs the current 2nd preview: the type of theScopedValue.callWhere
method’s operation parameter is a new functional interface that lets the Java compiler infer whether a checked exception may be thrown. Note that at the same time, ScopeValue.getWhere
is removed.
482: Flexible Constructor Bodies (Second Preview)
Introduced (and previewed) in JDK 22 as Statements before super(β¦), flexible constructor bodies let you initialize fields in the same class before invoking a constructor.
Before this change (specifically, once it was introduced for preview in JDK 22), super(...)
couldn’t be preceded by any other statements in a constructor. Since JDK 22, statements can come before super(...)
, making it easier to prepare, validate, and share arguments in constructors.
Constructors are still run in top-down order during the instantiation of the class. This provides you as a developer with some more freedom when expressing the behavior of constructors in your code.
Example for flexible constructor bodies:public class PositiveBigInteger extends BigInteger {
public PositiveBigInteger(long value) {
if (value <= 0) throw new IllegalArgumentException(...);
super(value);
}
}
JDK 23 changes and features
Overall, there’s good reason to be excited about the updates and new features in Java 23. We’re psyched about the 2nd preview of stream gatherers because this feature gives us as developers new ways to transform data. Structured concurrency is also great because it simplifies concurrent programming while improving reliability and enhancing observability. And we also like flexible constructor bodies because they let us fail fast when calling constructors while also guaranteeing that constructors run in top-down order. Finally, as avid markdown users, we’re also happy about markdown documentation comments because it provides a cleaner, easier way to edit documentation than HTML.
π€ 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
Sign up for our newsletter and follow us on social media: X, LinkedIn, and Facebook to make sure you never miss any of our upcoming content.