JDK 23 – News from the Java World

13.11.2024Tom Trapp
Tech Java jdk Web Development Stay Tuned Developer Experience Version Manager

Banner

After 22 comes? Exactly, 23! 🤯

In the latest TechUp, we want to take a closer look at the new version of the Java Development Kit, JDK 23, as we have done in recent years. 🕵️‍♂️

So let’s go through the JEPs (Java Enhancement Proposals) and take a look at the most exciting new features.

Of course you can find all code examples as always on GitHub. Feel free to take a look at all the examples and play around with them! 🚀

JEP-455: Primitive Types in Patterns, instanceof, and switch (Preview)

The feature behind JEP-455 is completely new and available in a first preview version. Now primitive types can be used in patterns, instanceof and switch. This allows you to work with primitive types more easily and efficiently.

Let’s directly compare two code examples, one with and one without JEP-455:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Without JEP-455
void main() {
	var number = 5;
	switch (number) {
		case 1 -> System.out.println("One");
		case 2 -> System.out.println("Two");
		default -> System.out.println("number " + number + " is not supported");
	}

	var littleNumber = 20;
	if (littleNumber >= -128 && littleNumber < 127) {
		var littleByteNumber = (byte) littleNumber;
		System.out.println("littleByteNumber: " + littleByteNumber);
	} else {
		System.out.println("littleNumber " + littleNumber + " is too big for a byte");
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// With JEP-455
void main() {
	var number = 5;
	switch (number) {
		case 1 -> System.out.println("One");
		case 2 -> System.out.println("Two");
		case int i when i > 10 -> System.out.println("number " + i + " is too high");
		case int i -> System.out.println("number " + i + " is not supported");
	}

	var littleNumber = 200;
	if (littleNumber instanceof byte littleByteNumber) {
		System.out.println("littleByteNumber: " + littleByteNumber);
	} else {
		System.out.println("littleNumber " + littleNumber + " is too big for a byte");
	}
}

As you can see, the code with JEP-455 is much shorter and easier to read. 🚀

Specifically, a case int i can now be used in the switch statement to use the value of the int. Furthermore, Pattern Matching can be used. Here we could also define a case byte b to use the value directly as a byte.

The same applies to the instanceof statement, here a byte can be defined directly to use the value.

JEP-466: Class-File API (Second Preview)

With JEP-466, the Class-File API was included as a preview feature for the second time. The Class-File API offers Java its own bytecode analysis and modification API. For example, all fields can be listed, methods and their dependencies can be listed, or even new methods or entire classes can be generated. This is certainly especially helpful for plugins etc.! Again, there were only minor technical changes and improvements in JDK 23.

Since we hardly looked at this JEP in my JDK 22 blog post, we now want to take a look under the hood!

With the Class-File API, classes can be read in a standardized way. Let’s take a closer look at all the methods and fields of the Integer class:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void main() {
	try (var in = Integer.class.getResourceAsStream("/java/lang/Integer.class")) {
		var classModel = ClassFile.of().parse(in.readAllBytes());

		System.out.println("Lets see the methods");
		classModel.methods().stream()
			.map(method -> method.methodName().stringValue())
			.map(methodName -> " - " + methodName)
			.forEach(System.out::println);

		System.out.println("Lets see the fields");
		classModel.fields().stream()
			.map(field -> field.fieldName().stringValue())
			.map(methodName -> " - " + methodName)
			.forEach(System.out::println);
	} catch (IOException e) {
		e.printStackTrace();
	}
}

It’s nice to see that we can read the class as a stream and then output the methods and fields.

It is important to mention here that the Class-File API is not a replacement or addition to the Reflection API, but a completely new API. It remains to be seen in which cases which API will be used.

With this API we can also create new Java classes, this could be interesting especially for plugins like an OpenAPI or Avro generator.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
import static java.lang.classfile.ClassFile.ACC_STATIC;
import static java.lang.constant.ConstantDescs.CD_String;
import static java.lang.constant.ConstantDescs.CD_void;

void main() throws IOException {
	var system = ClassDesc.of("java.lang", "System");
	var printStream = ClassDesc.of("java.io", "PrintStream");

	ClassFile.of().buildTo(
        Path.of("Hello.class"),
        ClassDesc.of("Hello"),
        classBuilder -> classBuilder
            .withMethodBody(
                "main",
                MethodTypeDesc.of(CD_void, CD_String.arrayType()),
                ACC_PUBLIC | ACC_STATIC,
                codeBuilder -> codeBuilder
                    .getstatic(system, "out", printStream)
                    .aload(codeBuilder.parameterSlot(0))
                    .iconst_0()
                    .aaload()
                    .invokevirtual(printStream, "println", MethodTypeDesc.of(CD_void, CD_String))
                    .return_()));
}

Let’s briefly go through the rough steps:

  • We define imports and classes from other packages to use them later
  • We build a new class Hello with a method public static main of type void that takes an array of strings
  • Then we use a code builder to generate the code
  • We implement the main method so that the first value of the array is printed

And then we can run the program like this:

1
java -cp . Hello Tom

Works! 🚀

I couldn’t think of a use case for myself personally, but I’m sure it will be very useful for many developers! For example in plugin development or for special tools.

JEP-467: Markdown Documentation Comments

Markdown is a widely used format for documenting code. With JEP-467, developers can now use Markdown documentation comments in their code. Now Java-Doc can be written with Markdown, specifically in CommonMark format. And of course including all Java-Doc markers like @param, @return, @throws and @see.

This makes it a) much easier for the developer to write the documentation and b) the documentation is more readable and beautiful.

Old:

1
2
3
4
5
6
7
/**
 * Returns the name of the person.
 * @return the name of the person.
 */
public String getName() {
    return name;
}

New:

1
2
3
4
5
/// Returns the name of the person.
/// @return the name of the person.
public String getName() {
    return name;
}

Of course, this is just a small example, you can find a more comprehensive example in the jdk-23 repository on GitHub.

img.png

Cool feature, also fully supported in e.g. IntelliJ IDEA! 🚀

JEP-469: Vector API (Eighth Incubator)

The JEP-469 is already in incubator status for the eighth time, with no changes compared to the last release!

The Vector API allows vector computations to be performed optimized for the corresponding CPU architecture.

This API continues to be blocked by the Valhalla Project. The Valhalla project aims to revolutionize Java development, with new value types, different memory usage and many other innovations! When the Valhalla project will be finalized is still unclear.

JEP-473: Stream Gatherers (Second Preview)

With JEP-473, it was made possible to modify and manipulate a stream using new intermediate operations.

We already took a close look at this function in the JDK 22 blog post! The second preview version is identical to the first preview version, there were no changes. Again, the goal here is to gather further experience and feedback from the community before finalizing the feature.

JEP-471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal

With JEP-471, the memory access methods in sun.misc.Unsafe have been marked as deprecated and will be removed in a later version. Since JDK 9 there is an alternative, which is also the current solution. The code should now be migrated as soon as possible.

You can find all information about this in the JEP description!

JEP-474: ZGC: Generational Mode by Default

With JEP-474 there were changes to the garbage collection.

Now the new garbage collector ZGC is final in generational mode! 🎉 By default, Java still uses the G1 Garbage Collector without any further configuration.

The ZGC can be activated with -XX:+UseZGC, new from JDK 23 then in Generational Mode. The non-generational mode will be removed in a later version.

If you want to learn more about ZGC, I can recommend this post: An Introduction to ZGC: A Scalable and Experimental Low-Latency JVM Garbage Collector.

For accurate benchmarks of the different garbage collectors, check out this blog post!

JEP-476: Module Import Declarations (Preview)

JEP-476 is another new feature in JDK 23. The first preview revolutionizes the way other classes are imported. Using import module <module-name>, all public classes are implicitly imported from the corresponding module and can be used.

For example, the java.sql module can be imported via import module java.sql; and all exported classes from that module can then be used.

If conflicts occur, i.e. if a class occurs in multiple packages, a compile error is thrown. In this case, the class must be imported explicitly.

This feature allows you to remove numerous imports, especially when import java.sql.* imports the entire package anyway. In addition, this feature allows Domain-Driven Imports to import entire modules instead of individual classes.

Of course, you can also define your own modules, for example for an internal SDK or similar. This then allows you to import the entire module of this SDK without having to import or know individual classes.

Cool feature, even cooler with the next feature! 🚀

JEP-477: Implicitly Declared Classes and Instance Main Methods (Third Preview)

We already know JEP-477 from JDK 21 and JDK 22, it was included in JDK 23 as a third preview. This feature allows you to define implicit classes with a main method, i.e. without a package and class name. You can find more information about this in my JDK 22 TechUp.

New with JDK 23, three imports print, printLn and readLn are imported by default from the java.io.IO package, so they do not need to be imported explicitly.

The readLn method allows you to store the user’s input in the terminal in a variable and then use it. This is a great new feature especially for beginners!

In addition, the base module is implicitly imported, so that all classes from the base module can be used without explicit importing. A total of 54 packages are included in the base module. However, this is “only” done for implicit classes and not for explicit classes.

JEP-477 makes it even easier to write and launch Java programs.

So a simple Java program can then look like this:

1
2
3
4
5
6
7
8
void main()
{
	var fruits = List.of("Apple", "Banana", "Cherry");
	println("Fruits: " + fruits);

	var name = readln("Enter your name: ");
	println("Your name is: " + name);
}

This is really a very slim and simple program, which is also very readable for beginners.

Thinking back to when I took my first steps with Java in 2012, this would have been a huge help! 🤯

The example from above would have looked like this back then:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package com.bnova.techhub.jdk23.jep477;

import java.util.List;
import java.util.Scanner;


public class TheVeryOldWay
{
	public static void main(String[] args)
	{
		List<String> fruits = List.of("Apple", "Banana", "Cherry");
		System.out.println("Fruits: " + fruits);

		Scanner scanner = new Scanner(System.in); 
		System.out.print("Enter your name: ");
		String name = scanner.nextLine();
		System.out.println("Your name is: " + name);
		scanner.close();
	}
}

Let’s take a quick look at the differences, they are enormous:

  • No package and class name needed anymore
  • No explicit importing of List and Scanner needed anymore
  • No public static void main(String[] args) needed anymore
  • No Scanner needed anymore, but readLn directly usable
  • No scanner.close() needed anymore
  • No System.out.println needed anymore, but println directly usable
  • much shorter and easier to read

And I had to write such classes by hand on paper back then and then type them out to learn and see if it worked. 🤯

Cool feature, isn’t it? 🚀

JEP-480: Structured Concurrency (Third Preview)

We already looked at and got to know the JEP-480 implementation in the JDK 22 TechUp. In short, it’s about simple and efficient management of threads and tasks.

The feature is available in the third preview version and brings no changes compared to the second preview version from JDK 22. The hope here from OpenJDK’s point of view is for even more feedback from the community. The feature would then be released in a final version.

Little inside look:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void main()
{
	ExecutorService executor = Executors.newFixedThreadPool(2);

	try (var scope = new StructuredTaskScope.ShutdownOnFailure())
	{
		System.out.println("Starting the search for Tom");
		Supplier<String> tom = scope.fork(UserSearcher::findTom);

		System.out.println("Starting the search for Tim");
		Supplier<String> tim = scope.fork(UserSearcher::findTim);

		System.out.println("Something is running in the background...");

		scope.join()            // Join both subtasks
				.throwIfFailed();  // ... and propagate errors
        ...

More on this here! 🚀

JEP-481: Scoped Values (Third Preview)

We also looked at and got to know JEP-481 in the JDK 22 TechUp. This is about setting and using values in a certain scope.

There was only one small, technical change compared to JDK 22, one method was converted to a functional interface.

JEP-482: Flexible Constructor Bodies (Second Preview)

We already know this JEP-482 from the JDK 22 TechUp, here it was included as a second preview with a new name Flexible Constructor Bodies.

There was only one change from the previous release: Now the constructor can initialize fields within the same class before explicitly calling another constructor. This allows a constructor in a subclass to ensure that the superclass constructor never executes code that sees the default value of a subclass field (e.g., 0, false, or null). This can happen if the superclass constructor, due to overriding, calls a method in the subclass that uses the field.

So now we can not only perform checks and logic before calling the super constructor, but also initialize fields.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class C extends Parent {

	private int number;
	private boolean bool;

	public C() {
		System.out.println("A constructor");
		this.number = 10;
		this.bool = true;
		super();
	}
}

You can find more about this in the JDK 23 repo! You can see classes A, B & C in the jep482 package as a little quiz! Take a look at them all and think beforehand what will be output! 🚀

Useful feature!

Removals

I presented one feature in my JDK 21 TechUp, and then again in my JDK 22 TechUp. Now it gets hard, hold on tight! 🥺

String templates have been removed from JDK 23. These were introduced in JDK 22 as a preview feature, but have been removed due to lack of interest and usage. This removal was based on community feedback and internal testing. However, they want to keep an eye on the topic and work on a better solution. Unfortunately, a good example of how to carefully consider the use of preview features.

In addition, there were other technical removals.

Conclusion

We are now exactly between two LTS versions, after the last LTS version with JDK 21, the journey is now slowly but surely heading towards JDK 25.

From my point of view, an exciting release, especially for newcomers, Java has been made even easier! Of course, this should be taken with a grain of salt and it is questionable how much these features really bring in an enterprise Java development.

I think the removal of the string templates is a pity. I am curious to see if or what will come as a successor.

Stay tuned! 🚀

This techup has been translated automatically by Gemini

Tom Trapp

Tom Trapp – Problemlöser, Innovator, Sportler. Am liebsten feilt Tom den ganzen Tag an der moderner Software und legt viel Wert auf objektiv sauberen, leanen Code.