Every new feature in Java 23
The next version of Java entered general availability phase, so let's go over all the new features it brings. Java 23 is the version that will be supported for the next six months—until March 2025. In the new release cycle, versions between LTS (Long-Term Support) releases are meant more for gradually testing new, larger features, so there aren't a lot of stable updates here. However, there are still a few, among the 12 JEPs (Java Enhancement Proposals) added in Java 23.
For reference, the changes are divided into:
- stable – polished and enabled by default for all users
- preview – relatively stable but still need refinement; changes are possible in future versions, and access requires configuring the appropriate flags
- incubator – experimental features that may undergo significant changes or be completely dropped in a future release; these also require enabling flags
New Documentation Format
In Java 23, JavaDoc documentation can now be written in Markdown (JEP 467), which is likely easier to read and write compared to the previous mix of HTML and @ tags.
All key Markdown features are supported, including links, tables, and code blocks, meaning that this comment should render nicely in the documentation:
/// Here is an example:
///
/// ```
/// /** Hello World! */
/// public class HelloWorld {
/// public static void main(String... args) {
/// System.out.println("Hello World!"); // the traditional example
/// }
/// }
/// ```
You can also still use JavaDoc tags, such as {@inheritDoc}, @param, and @return.
Further Changes to Pattern Matching
For the past two years, almost every new Java version has included changes to the pattern-matching mechanism. Java 23 is no different, with a new preview feature (JEP 455) that allows the use of primitive types in patterns, instance of and switch statements.
In short, this allows you to match patterns based on primitive types, saving unnecessary type casting.
For example, you can now do this:
switch (x) {
case int i -> System.out.println("It's an integer: " + i);
case double d -> System.out.println("It's a double: " + d);
}
With instanceof, this example from the documentation shows how it worked before:
int i = 1000;
if (i instanceof byte) { // false -- i cannot be converted exactly to byte
byte b = (byte)i; // potentially lossy
... b ...
}
And after:
if (i instanceof byte b) {
... b ... // no loss of information
}
Fewer Imports
A preview feature, JEP 476, introduces changes in module importing. The Java team noticed that most projects have too many lines starting with "import" at the top of files. This new change allows you to import all packages exported by a module. For example, the java.base
module exports packages like java.io
and java.lang
. So now, if you do:
import java.base;
It's like importing java.lang.*
, java.io.*
, and dozens of other packages from java.base
.
Already Familiar Features
Now for a list of improvements that were already present in previous Java versions, either as preview features or in the incubator.
JEP 466: Second preview of the Class-File API. This API facilitates reading and writing Java class files efficiently. It is set to replace ASM, making it easier to debug, modify, and generate classes in Java.
JEP 469: For the eighth (!) time in the incubator, we have the Vector API. Interestingly, no changes have been made this time; the work is simply on hold until language changes related to Project Valhalla are added to Java. Valhalla is a large refactor of Java, so we'll see how long this takes.
JEP 473: Second preview of Stream Gatherers. There are no changes from the previous version, as the Java team hasn't gathered enough feedback on this new feature. The Gatherer works similarly to a Collector but is designed to handle intermediate steps in stream processing. It also allows parallel processing, which is always a plus.
JEP 477: Implicitly declared classes and instance main methods —third time as a preview feature. This essentially shortens "HelloWorld" and reduces boilerplate when running a single Java file.
For example, now you can write:
void main() {
System.out.println("Hello, World!");
}
If you name the file HelloWorld.java
and run it with the command java HelloWorld.java
, the compiler will assume the class should be named according to the file name and run the main function.
JEP 480: Structured Concurrency—no changes, awaiting more feedback from the community. This feature allows breaking a large task into smaller steps, executed in separate threads. The whole thing is treated as a single unit of work, hence the name structured concurrency.
JEP 481: Scoped Values—third time as a preview, with minor changes. This proposal enables sharing immutable data between threads, which should be more efficient than using thread-local variables, especially in combination with structured concurrency.
JEP 482: Second time as a preview—more flexible constructor syntax. Previously, there weren't many possibilities for adding instructions before calling super()
or this()
. With the new syntax, you can add such instructions. This allows, for example, checking constructor arguments and throwing an exception if they don't make sense.
Smaller Changes
A stable update has come to ZGC (JEP 474). This garbage collector now defaults to generational mode. It turned out that this mode yields better results, and the non-generational mode will gradually be phased out.
Methods in sun.misc.Unsafe
have been marked for removal (JEP 471).
And that's all the changes in the new Java. As you can see, there are no groundbreaking updates. There's still a year until the next LTS release, and most of the preview features will likely become part of Java then. It's a shame we'll have to wait that long, as there are quite a few interesting updates.