Oracle released yet another version of Java with powerful enhancements for bytecode manipulation, tool development, code quality, and more. Here are the most interesting and useful Java 23 features – from the perspective of a Java Team Leader with 10+ years of experience in this technology.
As Java continues to evolve, each new release aims to introduce features that improve the language’s performance, usability, and flexibility. By adopting this release, you can stay ahead of the curve and prepare for when these features become stable in future LTS versions.
In this article, I will give you a quick overview of significant changes in Java 23, focusing on the most notable JEPs (JDK Enhancement Proposals). Those interested in previous changes can check out my earlier article on Java 21 features, and you can also read Dariusz Wawer’s older piece with a detailed description of Java 17 features (along with a comparison to Java 8).
For code examples, you have to add –enable-preview flag to your compiler args.
Primitive Types in Patterns, instanceof, and switch (Preview)
Primitive types have always been integral to Java, but handling them in patterns, instanceof, and switch constructs was limited. JEP 455 aims to extend pattern matching and the switch statement to support primitive types.
static String processInput(Object obj) { return switch (obj) { case Integer i -> "Integer value: %s".formatted(i); case Double d -> "Double value: %s".formatted(d); default -> "Unknown type"; }; } public static void main(String[] args) { System.out.println(processInput(10)); // Integer value: 10 System.out.println(processInput(5.5)); // Double value: 5.5 }
This enhancement allows developers to write cleaner and more concise code.
Class-File API (Second Preview)
Java’s class file format is crucial for bytecode manipulation and tool development. JEP 466 introduces the second preview of a new Class-File API, simplifying access to and manipulation of Java class files without requiring developers to rely on third-party libraries like ASM or BCEL.
This API will greatly benefit those working on frameworks, compilers, or tools that need to inspect or modify class files. With its straightforward design, it enhances flexibility while keeping developers closer to Java’s native mechanisms. You can find a simple example of interacting with a new API below:
public static void main(String[] args) throws IOException { ClassFile classFile = ClassFile.of(); ClassModel model=classFile.parse(Paths.get("/home/ExampleClass.class")); System.out.println("Class Name: " + model.thisClass()); // Class Name: 7 java23/ExampleClass model.methods().forEach( method -> System.out.println(" - " + method.methodName())); //- <init> //- sayHello }
Stream Gatherers (Second Preview)
Another preview feature brings very nice enhancements to the Java Stream API. As JEP473 documentation states, the main goals are to make stream pipelines more flexible and expressive and allow custom intermediate operations to manipulate streams of infinite size. Below are a few examples of built-in gathers operations:
Stream.of("A", "B", "C", "D", "E") .gather(Gatherers.fold(() -> "", (a, b) -> a + b)) .forEach(System.out::println); //ABCDE Stream.of("A", "B", "C", "D") .gather(Gatherers.windowFixed(2)) .forEach(System.out::println); //[A, B] //[C, D]
Of course, there is the possibility of creating your gatherers. To do that, you just have to implement the java.util.stream.Gatherer interface.
Scoped Values (Third Preview)
JEP 481 introduces scoped values, which are an alternative to thread-local variables. They provide a mechanism for sharing values within a specific scope, making it easier to work with multi-threaded applications. Let’s dive into code example:
public class Jep481ScopedValues { private static ScopedValue<String> X = ScopedValue.newInstance(); public static void main(String[] args) { foo(); } static void foo() { ScopedValue.runWhere(X, "foo", () -> bar()); } static void bar() { System.out.println("Printing X from bar(): " + X.get()); ScopedValue.runWhere(X, "bar", () -> baz()); System.out.println("Printing X from bar(): " + X.get()); } static void baz() { System.out.println("Printing X from baz(): " + X.get()); } } Output: Printing X from bar(): foo Printing X from baz(): bar Printing X from bar(): foo
Flexible Constructor Bodies (Second Preview)
JEP 482 revisits constructor flexibility in Java. Traditionally, constructor bodies in Java were limited in how they could be structured and how exceptions could be handled. This JEP introduces more flexibility, allowing developers to write more complex initialization logic within constructors, which enhances control over object creation.
public class Java481FlexibleConstructors extends BigInteger { Java481FlexibleConstructors(long value) throws Exception { if (value < 0) throw new Exception("Invalid value"); //that wasn’t possible before System.out.println("Initialized with value: " + value); super(String.valueOf(value)); } }
Other notable features in Java 23
In addition to the JEPs I’ve already covered, Java 23 introduces several other enhancements worth mentioning:
- Performance improvements (JEP 474: ZGC Generational Mode by Default): Java 23 optimizes the Z Garbage Collector by enabling its generational mode by default. This feature improves the efficiency of memory management, particularly for applications with long-running processes, by segregating young and old objects in the heap to enhance garbage collection performance
- Security updates (JEP 471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal): This update deprecates certain memory-access methods in sun.misc.Unsafe that were widely used for direct memory manipulation, enhancing the security of the platform.
- Library enhancements (JEP 467: Markdown Documentation Comments): JEP 467 introduces support for Markdown in Javadoc comments. This feature allows developers to write better-formatted and more readable documentation using Markdown syntax within their code comments, improving both internal and external documentation practices
- Module system improvements (JEP 476: Module Import Declarations): JEP 476 adds the ability to use import module declarations in Java source files. This simplifies module management, especially in multi-module projects, by allowing developers to import entire modules, not just individual classes or packages.
Summary
Java continues to evolve, and version 23 brings a wealth of improvements that will enhance the developer experience, from better handling of primitive types in pattern matching to more flexible constructors and advanced stream processing. Be sure to explore the changes and start incorporating them into your development workflow – as they will probably soon turn from preview features to core ones.
If you’re interested in more Java-related content (including deep dives into versions 21 and 17), check some of the other publications on our blog:
- Java 21 features: A detailed look at the most important changes in the new LTS release
- Java 17 features: A comparison between versions 8 and 17. What has changed over the years?
- JVM Kubernetes: Optimizing Kubernetes for Java Developers
- GitHub Copilot tutorial: We’ve tested it with Java and here’s how you can do it too
- How to code well? With good practices, a great team, and Java code quality tools
Java 23 features FAQ
What is the Java 23 release date?
Java 23 was officially released on September 17 2024.
Where can I download Java 23?
You can download Java from Oracle’s official website.
What is a primitive type in Java?
A primitive type is a basic data type that is not an object and holds a simple value.
What is pattern matching in Java?
Pattern matching lets you test whether an object has a specific structure and possibly extract data from it.
What is the stream API in Java?
It’s a tool you can use to process sequences of elements in a functional style.