DEV Community

Cover image for Modern Java Features (Java 21+) Explained with Examples
Madhan Kumar
Madhan Kumar

Posted on

Modern Java Features (Java 21+) Explained with Examples

Embrace simplicity, clarity, and power in your Java code:

Introduction:

If you have been writing Java for a while, you might have noticed something interesting. The language is not the same old verbose tool it once was. Java has been evolving and now offers a range of modern features that make your code simpler, cleaner, and more powerful.

In this article, we will walk through four standout features introduced in the latest Java versions, especially from Java 21 onward:

  • Record classes
  • Sealed classes
  • Pattern matching
  • Virtual threads

Each feature brings clarity and structure without adding unnecessary complexity. Let us explore them one by one with clear examples.

Modern Java Features

1. Record Classes: A Cleaner Way to Hold Data

  • Have you ever found yourself writing the same set of methods just to represent a simple data class? You know the drill: getters, constructors, equals, hashCode, and toString. It quickly becomes repetitive.

  • Record classes were introduced to solve exactly that problem. A record is a concise way to declare a class whose main purpose is to hold data.

public record Employee(String name, int id) {}
Enter fullscreen mode Exit fullscreen mode

This single line gives you:

  • An all-arguments constructor
  • Getter methods (like name() and id())
  • A proper equals() and hashCode() method
  • A readable toString() method

Records are immutable by default and perfect for modeling data that should not change once created.

Where to use: Data transfer objects, configuration models, or any class that exists to hold values.

2. Sealed Classes: Define a Closed Inheritance Model

  • Java has always supported inheritance, but it did not allow much control over who can extend your classes. With sealed classes, you can now restrict which classes are allowed to extend or implement a base type.

  • This is useful when you want to define a known and fixed set of subclasses.

public sealed class Shape permits Circle, Rectangle {}

public final class Circle extends Shape {}
public final class Rectangle extends Shape {}
Enter fullscreen mode Exit fullscreen mode
  • In the example above, only Circle and Rectangle are allowed to extend Shape. If any other class tries to do so, the compiler will throw an error.

Where to use: Domain models, state machines, and type-safe enums with custom logic.

3. Pattern Matching: Simpler Type Checks

  • Java’s pattern matching feature makes it easier and safer to work with types. It enhances the instanceof operator and switch statements by reducing the need for explicit casting.
  • This leads to cleaner, more readable code.
if (obj instanceof String s) {
    System.out.println("Length: " + s.length());
}
Enter fullscreen mode Exit fullscreen mode
  • This avoids repeating the type check and cast. It is simpler and safer.

Starting from Java 21, pattern matching also works within switch expressions:

static void describe(Object obj) {
    switch (obj) {
        case String s -> System.out.println("String of length " + s.length());
        case Integer i -> System.out.println("Integer value " + i);
        default -> System.out.println("Unknown type");
    }
}
Enter fullscreen mode Exit fullscreen mode

Where to use: Type inspection, serialization logic, message routing, and conditional logic based on object types.

4. Virtual Threads: Scalable Concurrency Made Easy

  • Concurrency in Java has traditionally been limited by the cost of platform threads. Managing thousands of threads came with significant overhead and complexity.
  • Virtual threads, introduced as a preview in earlier versions and finalized in Java 21, change the game. They are lightweight threads managed by the Java Virtual Machine, allowing you to run thousands or even millions of concurrent tasks without breaking a sweat.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10000).forEach(i ->
        executor.submit(() -> System.out.println("Task " + i))
    );
}
Enter fullscreen mode Exit fullscreen mode
  • This approach makes concurrent programming feel natural again. You write code just like you would with regular threads, but now it is far more efficient.

Where to use: High-throughput servers, parallel processing, input-output-heavy applications, and microservices.

Final Thoughts:

  • Java is no longer just the enterprise workhorse from the early 2000s. It has become modern, expressive, and a joy to work with. The introduction of record classes, sealed hierarchies, pattern matching, and virtual threads signals that Java is embracing clarity and simplicity without losing its core strengths.
  • If you are still using Java 8 or Java 11, consider upgrading. The features you’ll gain aren’t just nice to have; they are transformative
  • Keep your code elegant. Keep your logic clean. Let the language do more of the heavy lifting so you can focus on solving real problems.

Top comments (0)