Quantcast
Channel: Baeldung
Viewing all articles
Browse latest Browse all 3788

New Features in Java 25

$
0
0

1. Overview

Java 25 is almost out! The new short-term release, scheduled for September 2025, introduces a comprehensive set of enhancements across the Java language, standard libraries, APIs, and runtime.

In this tutorial, we’ll explore all the new features and changes introduced in Java 25 as of June 2025. Let’s understand them with a simple code sample and a detailed explanation.

2. Language and Compiler Features

Java 25 introduces a set of language and compiler enhancements aimed at making the language more expressive and concise. These features enhance the developer experience in both everyday tasks and advanced, modular scenarios.

2.1. Primitive Types in Patterns (JEP 507 – Third Preview)

Now, pattern matching can handle primitive types in switch and instanceof statements. For instance:

static void test(Object obj) {
    if (obj instanceof int i) {
        System.out.println("It's an int: " + i);
    }
}

JEP 507 introduces primitive types into Java’s pattern-matching framework, making such expressions more straightforward and reducing boilerplate code. This JEP is part of a broader effort to unify type patterns across the language.

2.2. Module Import Declarations (JEP 511 – Preview)

JEP 511 introduces Module Import Declarations, allowing us to use modules to declare their dependencies via import statements, improving modular readability. Traditionally, dependencies in a module were only declared in the module-info.java descriptor using requires directives. JEP 511 introduces a way to declare module dependencies using the import module statement at the top of a Java file, similar to traditional import statements. This enhances clarity and enables tools to infer dependencies during development with greater accuracy. For instance:

import module java.base; 
//...
public class Main {
    public static void main(String[] args) {
        Date d = new Date();
        System.out.println("Resolved Date: " + d);
    }
}

However, we must be careful with ambiguous references. Let’s see a sample code to demonstrate it:

import module java.base;      // exports java.util, which includes java.util.Date
import module java.sql;       // exports java.sql, which also includes java.sql.Date
public class Main {
    public static void main(String[] args) {
        Date d = Date.valueOf("2025-06-15");
        System.out.println("Resolved Date: " + d);
    }
}

When trying to compile this class, the following message will appear:

error: reference to Date is ambiguous
         Date d = Date.valueOf("2025-06-15");
         ^
   both class java.sql.Date in java.sql and class java.util.Date in java.util match
   error: reference to Date is ambiguous

The solution for this would be to add the import of the specific class that we want to use in this case:

import module java.base;
import module java.sql;
import java.sql.Date;
public class Main {
    public static void main(String[] args) {
        Date d = Date.valueOf("2025-06-15");
        System.out.println("Resolved Date: " + d);
    }
}

Finally, this change also allows us to switch from star imports to module imports:

// These imports could be coalesced:
import javax.xml.*; 
import javax.xml.parsers.*; 
import javax.xml.stream.*;

Instead, just use:

import module java.xml;

Although it is not recommended to abuse both import star and module imports, it does make the import and dependency definition more concise and less verbose.

2.3. Compact Source Files (JEP 512) and Instance Main Methods

Now Java supports top-level instance main methods and class-less compact files. That means now the following declaration is valid:

void main() {
    System.out.println("Hello from Java 25!");
}

JEP 512 builds on the simplified launcher introduced in Java 21. It allows us to write quick scripts or demos without class declarations. These compact source files are ideal for teaching, scripting, and rapid prototyping, lowering the barrier to entry for new developers and reducing the learning curve.

2.4. Flexible Constructor Bodies (JEP 513 – Final)

Flexible Constructor Bodies (JEP 513) enables multiple constructors to delegate to a common initialization body, making it as simple as that:

class Person {
    final int age;
    Person(int age) {
        this.age = age;
    }
}
class Employee extends Person {
    final String name;
    Employee(String name, int age) {
        if (age < 18 || age > 67)
            throw new IllegalArgumentException("Age must be between 18 and 67");
        super(age); // super() is no longer required as the first statement in Java 25
        this.name = name;
    }
    public static void main(String[] args) {
        var emp = new Employee("Alice", 35);
        System.out.println("Person age set: " + emp.age);
    }
}

Before JEP 513, Java constructors were required to invoke super(…) or this(…) as the very first statement, which often forced us to duplicate validation or initialization logic, or push it into static helper methods. With JEP 513, we can include code before the constructor invocation, allowing argument validation or shared setup to happen cleanly in one place, improving readability, fail-fast behavior, and object integrity without breaking Java’s construction rules.

3. API Enhancements

Java 25 also brings a set of improvements for the existing APIs, in addition to continuing work on new APIs that are still in preview, final, and early stages.

3.1. Scoped Values (JEP 506 – Final)

JEP 506 offers lightweight, immutable, thread-safe alternative to ThreadLocal. Those are designed to work in conjunction with Virtual Threads:

import java.lang.ScopedValue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ScopedUserExample {
    static final ScopedValue<String> USER = ScopedValue.newInstance();
    public static void main(String[] args) {
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            executor.submit(() -> ScopedValue.where(USER, "Alice").run(() -> {
                System.out.println("Thread: " + Thread.currentThread());
                System.out.println("User: " + USER.get());
            }));
            executor.submit(() -> ScopedValue.where(USER, "Bob").run(() -> {
                System.out.println("Thread: " + Thread.currentThread());
                System.out.println("User: " + USER.get());
            }));
            // Optional delay to ensure output appears before main exits
            Thread.sleep(200);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Scoped Values are designed to pass context across call chains in a safe, performant, and immutable way. They work particularly well with virtual threads and structured concurrency, offering an efficient alternative to ThreadLocal by avoiding memory leaks and synchronization overhead.

Note: When we use Scoped Value with Virtual Threads, the logic that accesses the scoped value must be wrapped inside the ScopedValue.where(…).run(…) scope. Submitting a task to an executor within the scope is not enough. The task itself must be created inside the scope to retain the binding.

3.2. Structured Concurrency (JEP 505 – Fifth Preview)

JEP 505 aims to simplify concurrency by treating related threads as a single unit with proper lifecycle management. The fifth preview refines the API by replacing constructors and separate policy methods with a single static factory method: StructuredTaskScope.open(). This approach improves consistency and flexibility for defining custom join and error-handling behaviors. Next, we’ll use the new syntax:

import java.util.concurrent.StructuredTaskScope;
public class StructuredExample {
    static String fetchUser() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "Alice";
    }
    static String fetchOrder() {
        try {
            Thread.sleep(150);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "Order#42";
    }
    public static void main(String[] args) throws Exception {
        try (var scope = StructuredTaskScope.<String>open()) {
            var userTask = scope.fork(() -> fetchUser());
            var orderTask = scope.fork(() -> fetchOrder());
            scope.join();
            System.out.println(userTask.get() + " - " + orderTask.get());
        }
    }
}

Structured concurrency helps us manage multiple concurrent tasks that are logically correlated. It guarantees that child threads complete or are cancelled as a group, improving reliability and readability in multi-threaded applications.

3.3. Stable Value API (JEP 502 – Preview)

The Stable Value API (JEP 502) extends the Optional-like semantics to context-stable immutable values:

import java.lang.StableValue;
public class StableExample {
    public static void main(String[] args) {
        // Create a new unset StableValue
        var greeting = StableValue.<String>of();
        String message = greeting.orElseSet(() -> "Hello from StableValue!");
        System.out.println(message);
    }
}

Stable values offer an API for safely sharing immutable, context-stable values across threads or computations. They’re handy in situations involving caching, lazy evaluation, or consistent reads within a stable scope, and integrate well with structured concurrency.

3.4. PEM Encodings of Cryptographic Objects (JEP 470 – Preview)

JEP 470 adds support for reading and writing cryptographic keys and certificates in PEM format via standard APIs. The new API abstracts such operations, making them as simple as:

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class PEMExample {
    public static void main(String[] args) {
      String pem = """
        -----BEGIN PUBLIC KEY-----
        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgjDohS0RHP395oJxciVaeks9N
        KNY5m9V1IkBBwYsMGyxskrW5sapgi9qlGSYOma9kkko1xlBs17qG8TTg38faxgGJ
        sLT2BAmdVFwuWdRtzq6ONn2YPHYj5s5pqx6vU5baz58/STQXNIhn21QoPjXgQCnj
        Pp0OxnacWeRSnAIOmQIDAQAB
        -----END PUBLIC KEY-----
        """;
        try {
            String base64 = pem.replaceAll("-----.*-----", "").replaceAll("\\s", "");
            byte[] keyBytes = Base64.getDecoder().decode(base64);
            X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
            KeyFactory factory = KeyFactory.getInstance("RSA");
            PublicKey key = factory.generatePublic(spec);
            System.out.println("Loaded key: " + key.getAlgorithm());
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            e.printStackTrace();
        }
    }
}

Now we can utilize the Java Security API to handle PEM-encoded objects, such as X.509 certificates and RSA keys, directly, eliminating the need for third-party libraries or manual conversions. This improves interoperability with OpenSSL-based systems and streamlines secure communications.

3.5. Vector API (JEP 508 – Tenth Incubator)

JEP 508 provides an API to express vector computations that reliably compile to optimal vector hardware instructions:

import jdk.incubator.vector.*;
public class VectorExample {
    public static void main(String[] args) {
        float[] left = {1f, 2f, 3f, 4f};
        float[] right = {5f, 6f, 7f, 8f};
        FloatVector a = FloatVector.fromArray(FloatVector.SPECIES_128, left, 0);
        FloatVector b = FloatVector.fromArray(FloatVector.SPECIES_128, right, 0);
        FloatVector c = a.add(b);
        float[] result = new float[FloatVector.SPECIES_128.length()];
        c.intoArray(result, 0);
        System.out.println("Vector result: " + java.util.Arrays.toString(result));
    }
}

Requires: –enable-preview –add-modules jdk.incubator.vector

The Vector API enables data-parallel computations that can be executed efficiently on modern central processing units (CPUs). It helps Java code achieve performance comparable to hand-tuned native code by utilizing SIMD instructions, and continues to evolve through incubator stages.

3.6. Key Derivation Function API (JEP 510 – Final)

Java 25 introduces a standard API for password-based key derivation functions, such as PBKDF2 and scrypt:

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class KeyDerivationExample {
    public static void main(String[] args) throws Exception {
        char[] password = "hunter2".toCharArray();
        byte[] salt = "somesalt".getBytes();
        PBEKeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        SecretKey key = factory.generateSecret(spec);
        System.out.println("Derived key format: " + key.getFormat());
    }
}

JEP 510 standardizes access to widely used cryptographic primitives for deriving encryption keys from user passwords, reducing reliance on third-party libraries and enabling secure implementations out of the box.

4. Other Changes

Java 25 changes also include the removal of configurations, platform-based updates, and enhancements.

4.1. Remove the 32-bit x86 Port (JEP 503 – Final)

JEP 503 removes the support for the legacy 32-bit x86 architecture from OpenJDK. This JEP eliminates maintenance overhead for a platform with dwindling relevance. The 64-bit x86 and ARM64 ports remain fully supported.

4.2. JFR CPU-Time Profiling (JEP 509 – Experimental)

JEP 509 adds CPU-time-based profiling support to Java Flight Recorder (JFR). This feature enables us to record and analyze the amount of CPU time spent in specific methods or threads, thereby improving performance diagnostics, particularly in multi-threaded and I/O-bound workloads. Use the new JDK.CPULoad and related JFR events with custom recordings:

java
  -XX:StartFlightRecording=filename=cpu-time.jfr,duration=10s,settings=profile
  --enable-preview
  MyApp>

Then analyze the CPU time.JFR file in JDK Mission Control or VisualVM to observe CPU usage per method and thread.

4.3. Ahead-of-Time Command-Line Ergonomics (JEP 514 – Final)

Ahead-of-Time Command-Line Ergonomics (JEP 514) improves startup performance with ergonomic command-line options for AOT (ahead-of-time) compilation. Java 25 makes it easier to use jaotc and GraalVM-based AOT tooling, enabling better startup performance for static deployments and native-image-based scenarios.

4.4. Ahead-of-Time Method Profiling (JEP 515 – Final)

JEP-515 enables method-level profiling during AOT compilation for better optimization decisions. It improves the performance of AOT-compiled Java programs by incorporating method usage frequency into the compilation strategy. Also, it helps the AOT engine make smarter inlining and optimization decisions.

4.5. JFR Cooperative Sampling (JEP 518 – Experimental)

JEP 518 allows applications to suggest safe sampling points to Java Flight Recorder. Cooperative sampling reduces overhead by aligning sampling with application-defined safe points, improving accuracy while minimizing disruption to performance-sensitive code.

4.6. Compact Object Headers (JEP 519 – Preview)

JEP 519 reduces object header size on 64-bit architectures. This change shrinks the memory footprint of Java objects by using compact layouts for synchronization and identity data in object headers. It particularly benefits large heaps and microservice environments.

4.7. JFR Method Timing and Tracing (JEP 520 – Experimental)

JEP 520 enhances JFR with detailed per-method timing and call tracing information. We developers gain finer-grained visibility into which methods consume the most time, allowing for deeper profiling and better bottleneck identification.

4.8. Generational Shenandoah (JEP 521 – Final)

JEP 521 adds generational support to the Shenandoah garbage collector. Generational GC improves throughput and pause time performance by optimizing young-generation collection separately from long-lived objects. It brings Shenandoah in line with collectors like G1 and ZGC in terms of efficiency.

5. What Developers Need to Know

As we just saw, many features in Java 25 are still in preview or incubator stage. To compile and run the code using these features, we must enable them. We already did in preview code snippets, but it’s worth understanding it better:

  • –enable-preview: required for all preview features, otherwise compile errors will appear
  • –add-modules <name>: required for incubating modules, such as the jdk.incubator.vector we used earlier
  • –release 25: recommended during compilation to target the Java 25 platform

Be aware that preview and incubator APIs may change or be removed in future versions. We should avoid using them in production or stay up to date with the official JDK documentation and release notes to look out for bug fixes or problems. Having this in mind, to compile and run code with such features, we need:

# At compile time do
javac --enable-preview --release 25 --add-modules jdk.incubator.vector MyClass.java
# At runtime do
java --enable-preview --add-modules jdk.incubator.vector MyApp

This way we hint the Java Virtual Machine (JVM) to allow the use of such features at compile and runtime.

6. Conclusion

Java 25 continues the platform’s steady evolution toward modernization and efficiency. It refines many preview features, introduces new APIs, and improves performance at every level, from language syntax to runtime diagnostics and memory management.

This release also highlights Java’s commitment to modern hardware, including profiling and additional features. Although Java 25 is a short-term release, it offers valuable advantages and new capabilities that make it worthwhile for developers to adopt and provide feedback on this feature, as this will shape the future LTS version, such as Java 27. The complete list of changes in Java 25 is in the JDK release notes.

The post New Features in Java 25 first appeared on Baeldung.
       

Viewing all articles
Browse latest Browse all 3788

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>