Java 21: New Features and Examples

Introduction

Java 21 is the latest long-term support (LTS) release of the Java platform, released in September 2023. It includes a number of new features and enhancements.

  • Record classes: Record classes are a new type of class that is specifically designed to represent immutable data structures. They are more concise and easier to use than traditional classes for this purpose.
  • Sealed classes: Sealed classes are a new type of class that can only be extended by a specific set of subclasses. This can help to improve the safety and security of code by preventing unexpected subclasses from being created.
  • Pattern matching for instanceof: Pattern matching can now be used to check the type of an object in a more concise and expressive way.
  • Virtual threads: Virtual threads are a new type of thread that is much more efficient than traditional threads. They can be used to scale applications to a larger number of concurrent tasks without sacrificing performance.
  • Sequenced collections: Sequenced collections are a new type of collection that provides a guaranteed order of iteration. This can be useful for applications that need to process collections in a specific order.
  • Record patterns: Record patterns are a new type of pattern that can be used to deconstruct record values in a concise and expressive way.
  • String templates: String templates are a new way to format strings in Java that is more concise and easier to use than traditional string formatting.
  • Unnamed patterns and variables: Unnamed patterns and variables are a new feature that allows you to declare patterns and variables without having to give them a name. This can be useful for concise and expressive code.
  • Unnamed classes and instance main methods: Unnamed classes and instance main methods are a new feature that allows you to create and run classes without having to give them a name. This can be useful for one-off tasks or for creating anonymous classes.
  • Scoped values: Scoped values are a new feature that allows you to declare and use values that are only accessible within a specific scope. This can be useful for preventing memory leaks and for improving the modularity of code.
  • Structured concurrency: Structured concurrency is a new feature that provides a more structured and easier-to-use way to write concurrent code. It is still under development, but it is expected to be finalized in a future release of Java.

In this article, we will take a closer look at each of these new features and provide examples of how to use them.

Record Classes

Record classes are a new type of class that is specifically designed to represent immutable data structures. They are more concise and easier to use than traditional classes for this purpose.

To create a record class, you simply use the record keyword followed by the name of the class and a list of fields. The fields can be of any type, but they must all be final.

For example, the following code shows how to create a record class to represent a person.

record Person(
    String name,
    int age,
    Address address
) {}

This creates a record class with three fields: name, age, and address. The fields are all final, which means that they cannot be modified once they have been initialized.

Record classes also have a number of other features that make them useful for representing immutable data structures. For example, they automatically generate getters, setters, equals(), hashCode(), and toString() methods. They also support pattern matching, which makes it easy to deconstruct and work with record values.

Here is an example of how to use a record class.

// Create a new Person record.
Person person = new Person("Sarthak Varshney", 24, new Address("123 Main Street", "Uttar Pradesh", "IN", "243720"));

// Get the person's name.
String name = person.name();

// Print the person's address.
System.out.println(person.address());

Output

Sealed Classes

Sealed classes are a new type of class that can only be extended by a specific set of subclasses. This can help to improve the safety and security of code by preventing unexpected subclasses from being created.

To create a sealed class, you use the sealed keyword followed by the name of the class and a list of subclasses. The subclasses can be of any type, but they must all be nested within the sealed class.

For example, the following code shows how to create a sealed class to represent different types of animals.

sealed class Animal permits Dog, Cat, Bird {}

This creates a sealed class called Animal With three subclasses: Dog, Cat, and Bird. No other classes can extend the Animal class.

Sealed classes can be used in a number of ways. For example, they can be used.

  • Implement a type hierarchy in a more safe and secure way.
  • Prevent unexpected subclasses from being created, which can lead to bugs and security vulnerabilities.
  • Improve the performance of pattern matching by reducing the number of cases that need to be considered.

Here is an example of how to use a sealed class.

// Create a new Dog object.
Dog dog = new Dog("Fido");

// Check if the animal is a dog.
if (animal instanceof Dog) {
    // Do something specific to dogs.
}

This code will only compile if the animal variable is of type Dog or a subclass of Dog. Otherwise, the code will not compile.

Pattern matching for instanceof

Pattern matching can now be used to check the type of an object in a more concise and expressive way.

For example, the following code shows how to use pattern matching to check if an object is a Dog and, if so, get its name.

// Create a new Dog object.
Dog dog = new Dog("Fido");

// Check if the animal is a dog.
String name = switch (animal) {
    case Dog d -> d.name();
    default -> "Unknown animal";
};

// Print the dog's name.
System.out.println(name);

Output

This code is more concise and expressive than the traditional way of checking the type of an object with instanceof and then casting the object to the appropriate type.

Virtual Threads

Virtual threads are a new type of thread that is much more efficient than traditional threads. They can be used to scale applications to a larger number of concurrent tasks without sacrificing performance.

Virtual threads are implemented by the Java runtime rather than the operating system. This means that the operating system does not need to keep track of each virtual thread, which saves resources and improves performance.

Virtual threads are also very lightweight. They can be created and destroyed very quickly, which makes them ideal for applications that need to handle a large number of concurrent tasks.

Here is an example of how to use virtual threads.

// Create a new virtual thread pool.
VirtualThreadPool virtualThreadPool = new VirtualThreadPool();

// Create a new virtual thread and submit a task to it.
VirtualThread virtualThread = virtualThreadPool.start();
virtualThread.submit(() -> {
    // Do something.
});

This code will create a new virtual thread and submit a task to it. The task will be executed in the virtual thread pool.

Record Patterns

Record patterns are a new type of pattern that can be used to deconstruct record values in a concise and expressive way.

For example, the following code shows how to use a record pattern to deconstruct a Person record value into its individual components:

record Person(String name, int age, Address address) {}

// Deconstruct the Person record value into its individual components.
Person person = new Person("Sarthak Varshney", 24, new Address("123 Main Street", "Uttar Pradesh", "IN", "243720"));
String name = person switch {
    case Person(n, a, _) -> n;
};

// Print the person's name.
System.out.println(name);

Output

String templates

String templates are a new way to format strings in Java that is more concise and easier to use than traditional string formatting.

For example, the following code shows how to use a string template to format a greeting.

// Create a string template.
String template = "Hello, ${name}!";

// Bind the name variable to the string template.
String greeting = template.bindString("name", "Sarthak Varshney");

// Print the greeting.
System.out.println(greeting);

Output

Unnamed patterns and variables

Unnamed patterns and variables are a new feature that allows you to declare patterns and variables without having to give them a name. This can be useful for concise and expressive code.

For example, the following code shows how to use an unnamed pattern to check if a string is empty.

// Create an unnamed pattern to match an empty string.
Pattern emptyStringPattern = Pattern.compile("");

// Check if the string is empty.
boolean isEmpty = string.matches(emptyStringPattern);

// Do something if the string is empty.
if (isEmpty) {
    // Do something.
}

Unnamed classes and instance main methods

Unnamed classes and instance main methods are a new feature that allows you to create and run classes without having to give them a name. This can be useful for one-off tasks or for creating anonymous classes.

For example, the following code shows how to create and run an unnamed class to print "Hello, world!" to the console.

// Create an unnamed class with an instance main method.
new Object() {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}.main(null);

Scoped values

Scoped values are a new feature that allows you to declare and use values that are only accessible within a specific scope. This can be useful for preventing memory leaks and for improving the modularity of code.

For example, the following code shows how to use a scoped value to prevent a memory leak.

// Declare a scoped value.
ScopedValue<DatabaseConnection> connection = ScopedValue.create(() -> new DatabaseConnection());

// Use the database connection.
connection.get().query("SELECT * FROM users");

// Close the database connection.
connection.close();

The database connection will be automatically closed when the connection variable goes out of scope. This prevents the database connection from being leaked and wasting resources. 

Structured concurrency

Structured concurrency is a new feature that provides a more structured and easier-to-use way to write concurrent code. It is still under development, but it is expected to be finalized in a future release of Java.

Structured concurrency is based on the idea of using asynchronous tasks to represent concurrent work. Asynchronous tasks are tasks that can be executed concurrently without blocking the main thread.

Structured concurrency provides a number of benefits.

  • It makes concurrent code easier to write and maintain.
  • It helps to prevent common concurrency bugs, such as deadlocks and race conditions.
  • It can improve the performance of concurrent applications.

Here is an example of how to use structured concurrency.

// Create a new asynchronous task.
AsyncTask task = new AsyncTask(() -> {
    // Do something.
});

// Start the asynchronous task.
task.start();

// Wait for the asynchronous task to finish.
task.await();

This code will start an asynchronous task and then wait for it to finish. The asynchronous task will be executed concurrently with the main thread.

  • Improved performance of lambda expressions: Lambda expressions are now faster than ever in Java 21. This is due to a number of optimizations, including improved code generation and faster runtime support.
  • Support for TLS 1.3: Java 21 now supports TLS 1.3, the latest version of the TLS protocol. This provides improved security and performance for network communication.
  • Support for JDK Flight Recorder: JDK Flight Recorder is a new tool that can be used to collect and analyze data about the performance of Java applications. Java 21 now provides native support for JDK Flight Recorder, making it easier to use and more efficient.
  • Improved support for native code: Java 21 includes a number of improvements to the way that Java interacts with native code. This makes it easier to develop high-performance Java applications that need to interact with native libraries.
  • New APIs for working with JSON and XML: Java 21 includes a number of new APIs for working with JSON and XML. These APIs make it easier to parse and generate JSON and XML data in Java.

Note. Overall, Java 21 is a significant release that includes a number of new features and improvements. These features make Java more concise, expressive, safe, and performant.

Example of using JDK Flight Recorder

To use JDK Flight Recorder, you first need to enable it. You can do this by setting the -XX:StartFlightRecording=duration=60s JVM option. This will start a recording that will last for 60 seconds.

Once the recording has finished, you can use the jfr tool to analyze it. The jfr tool is included in the JDK.

For example, the following command will start a recording and then analyze it.

jfr record start
sleep 60
jfr record stop
jfr print

This will print a report to the console that shows various performance metrics, such as CPU usage, memory usage, and garbage collection activity.

Example of using new APIs for working with JSON and XML

The following code shows how to use the new JSON API to parse a JSON string.

import javax.json.Json;
import javax.json.JsonObject;

// Create a JSON string.
String jsonString = "{"
    + " \"name\": \"Sarthak Varshney\","
    + " \"age\": 24"
    + "}";

// Parse the JSON string into a JsonObject object.
JsonObject jsonObject = Json.createReader(jsonString).readObject();

// Get the person's name from the JsonObject object.
String name = jsonObject.getString("name");

// Print the person's name.
System.out.println(name);

Output

The following code shows how to use the new XML API to parse an XML string.

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;

// Create an XML string.
String xmlString = "<person>"
    + " <name>Sarthak Varshney</name>"
    + " <age>24</age>"
    + "</person>";

// Parse the XML string into a Document object.
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = documentBuilder.parse(xmlString);

// Get the person's name from the Document object.
String name = document.getElementsByTagName("name").item(0).getTextContent();

// Print the person's name.
System.out.println(name);

 Output

These are just a few examples of the new features in Java 21. I encourage you to explore the documentation to learn more about all of the new features and improvements.

Conclusion

Java 21 is a major release that includes a number of new features and improvements that make Java more concise, expressive, safe, and performant.

One of the most significant new features is record classes. Record classes are a new type of class that is specifically designed to represent immutable data structures. They are more concise and easier to use than traditional classes for this purpose.

Another important new feature is sealed classes. Sealed classes are a new type of class that can only be extended by a specific set of subclasses. This can help to improve the safety and security of code by preventing unexpected subclasses from being created.

Java 21 also includes a number of other new features.

  • Pattern matching for instanceof: Pattern matching can now be used to check the type of an object in a more concise and expressive way.
  • Virtual threads: Virtual threads are a new type of thread that is much more efficient than traditional threads. They can be used to scale applications to a larger number of concurrent tasks without sacrificing performance.
  • Sequenced collections: Sequenced collections are a new type of collection that provides a guaranteed order of iteration. This can be useful for applications that need to process collections in a specific order.
  • Record patterns: Record patterns are a new type of pattern that can be used to deconstruct record values in a concise and expressive way.
  • String templates: String templates are a new way to format strings in Java that is more concise and easier to use than traditional string formatting.
  • Unnamed patterns and variables: Unnamed patterns and variables are a new feature that allows you to declare patterns and variables without having to give them a name. This can be useful for concise and expressive code.
  • Unnamed classes and instance main methods: Unnamed classes and instance main methods are a new feature that allows you to create and run classes without having to give them a name. This can be useful for one-off tasks or for creating anonymous classes.
  • Scoped values: Scoped values are a new feature that allows you to declare and use values that are only accessible within a specific scope. This can be useful for preventing memory leaks and for improving the modularity of code.

Overall, Java 21 is a significant release that includes a number of new features and improvements that make Java a more powerful and versatile language.


Similar Articles
Ezmata Technologies Pvt Ltd
You manage your core business, while we manage your Infrastructure through ITaaS. It’s a game chan