You are here
Home > java > Core Java >

Java 27 New Features With Examples

Java 27 New Features With Examples

Java 27 New Features With ExamplesJava 27, released September 15, 2026, is the second non-LTS release after Java 25, shipping nine JEPs. Finalized features include G1 as the default garbage collector everywhere, Compact Object Headers by default, post-quantum TLS 1.3 key exchange, and JFR data redaction. Five more JEPs mature further preview and incubator rounds.

In this article, we will walk through every Java 27 new feature with practical, runnable examples.

You may also go through Java 26 New Features With Examples if you want the full picture of what changed just six months before this release.

What Changed in Java 27?

Java 27 keeps the six-month cadence going: Java 25 (LTS, September 2025), Java 26 (March 2026), and now Java 27 (September 2026). Like Java 26, it is a short-term release, supported for six months, while Java 25 remains the release most production teams should still be standardizing on. Java 27 ships nine JEPs across four categories.

Java 27 at a Glance

JEP Feature Category Status
JEP 523 Make G1 the Default Garbage Collector in All Environments HotSpot Final (permanent)
JEP 527 Post-Quantum Hybrid Key Exchange for TLS 1.3 Security Library Final (permanent)
JEP 531 Lazy Constants Core Java Library Preview (3rd)
JEP 532 Primitive Types in Patterns, instanceof, and switch Java Language Spec Preview (5th)
JEP 533 Structured Concurrency Core Java Library Preview (7th)
JEP 534 Compact Object Headers by Default HotSpot Final (permanent)
JEP 536 JFR In-Process Data Redaction HotSpot Final (permanent)
JEP 537 Vector API Core Java Library Incubator (12th)
JEP 538 PEM Encodings of Cryptographic Objects Security Library Preview (3rd)

Four JEPs are finalized and usable without any special flag: JEP 523, JEP 527, JEP 534, and JEP 536. The remaining five are still previews or an incubator module, which means their APIs can still shift before they are locked in for good.

Why Does Java 27 Matter for Developers?

Even on a short-term release, two of these four finalized JEPs quietly change behavior your team never explicitly asked for: Compact Object Headers now shrink every object on the heap by default, and G1 becomes the default collector even in tiny, single-CPU containers that used to get Serial GC.

In a production Spring Boot service running in a resource-constrained container, upgrading the base image to JDK 27 alone can change your GC and memory profile without a single line of application code changing, so it is worth measuring in staging rather than assuming “nothing changed” just because the build stayed green. On the security side, JEP 527 is the more strategic one: teams with a post-quantum readiness mandate now get TLS 1.3 hybrid key exchange for free, with zero code changes, as long as they have not hard-coded specific key exchange groups.

JEP 532: Primitive Types in Patterns, instanceof, and switch (Fifth Preview)

Now in its fifth preview round since first appearing in Java 23, this feature lets primitives like int, double, and boolean participate directly in instanceof and switch patterns, as long as the value converts safely and without loss.

public class PrimitivePatternDemo {
    static String classify(Object value) {
        return switch (value) {
            case int i when i < 0 -> "negative int: " + i;
            case int i -> "non-negative int: " + i;
            case double d -> "a double: " + d;
            default -> "something else";
        };
    }

    public static void main(String[] args) {
        System.out.println(classify(-5));
        System.out.println(classify(42));
        System.out.println(classify(3.14));
        System.out.println(classify("hello"));
    }
}

Output:

negative int: -5
non-negative int: 42
a double: 3.14
something else

Five preview rounds in, the API shape is stable; what has mostly changed release to release are dominance checks and exactness rules the compiler uses to reject ambiguous or lossy patterns, catching more mistakes at compile time rather than at run time.

JEP 531: Lazy Constants (Third Preview)

Lazy Constants (formerly Stable Values in Java 25, renamed in Java 26’s JEP 526) reach their third preview round in Java 27. The core LazyConstant<T> API you saw in Java 26 is unchanged: define a final field of type LazyConstant<T>, hand it a Supplier<T>, and call get() whenever you actually need the value.

public class UserController {

    private final LazyConstant<LoginService> loginService;

    public UserController() {
        this.loginService = LazyConstant.of(LoginService::initialize);
    }

    public void handleLogin() {
        loginService.get().authenticate();
    }
}

This preview round trims the API further: the low-level isInitialized() and orElse() methods are gone, keeping get() as the one method you actually call. On the collections side, List.ofLazy(…) and Map.ofLazy(…) from Java 26 are now joined by a new Set.ofLazy(…) factory, so a fixed set of possible members can each be computed lazily instead of eagerly.

Manual Lazy Initialization vs. LazyConstant (Java 27)

Aspect Manual (double-checked locking) LazyConstant (Java 27, preview)
Thread safety You write it yourself Built in
API surface N/A get() only; no orElse()/isInitialized()
Lazy collections Hand-rolled caching map List.ofLazy, Map.ofLazy, Set.ofLazy
JVM constant-folding Not guaranteed Supported when held in a final field

JEP 533: Structured Concurrency (Seventh Preview)

Structured Concurrency treats a group of related subtasks as one unit of work: fork them together, join them together, and if one fails, the rest are cancelled automatically. The seventh preview keeps the StructuredTaskScope.open() shape from Java 26 working exactly as before, while refining how exceptions are typed when they propagate out of join().

import java.util.concurrent.StructuredTaskScope;

public class StructuredConcurrencyDemo {

    record InventoryInfo(int unitsLeft) {}
    record PricingInfo(double price) {}

    static InventoryInfo fetchInventory() throws InterruptedException {
        Thread.sleep(80);
        return new InventoryInfo(24);
    }

    static PricingInfo fetchPricing() throws InterruptedException {
        Thread.sleep(120);
        return new PricingInfo(49.99);
    }

    public static void main(String[] args) throws Exception {
        try (var scope = StructuredTaskScope.open()) {
            var inventoryTask = scope.fork(StructuredConcurrencyDemo::fetchInventory);
            var pricingTask = scope.fork(StructuredConcurrencyDemo::fetchPricing);

            scope.join();

            System.out.println("Inventory: " + inventoryTask.get());
            System.out.println("Pricing: " + pricingTask.get());
        }
    }
}

Output:

Inventory: InventoryInfo[unitsLeft=24]
Pricing: PricingInfo[price=49.99]

The headline change in this round is that StructuredTaskScope and Joiner now carry a third type parameter describing exactly which exception type join() can throw, so callers get compile-time clarity on failure handling instead of catching a broad, generic exception type.

JEP 523: Make G1 the Default Garbage Collector in All Environments

Before Java 27, the HotSpot JVM had a quiet exception baked in: if your application was allocated a single CPU or less than 1,792 MB of physical memory, the JVM silently switched to Serial GC instead of G1, based on benchmarks from years ago. JEP 523 removes that exception. G1 is now the default collector everywhere, regardless of core count or memory size, because G1’s footprint and throughput have improved enough in recent releases (including the JDK 26 synchronization work) to outperform Serial GC even in small containers.

# A 1-CPU, 512MB container on Java 26 and earlier silently used Serial GC.
# On Java 27, the same container now uses G1 by default:
java -jar your-application.jar

# Confirm which collector is active:
java -XX:+PrintFlagsFinal -version | grep -i UseG1GC

Output:

bool UseG1GC = true {product} {default}

If your workload was actually tuned around Serial GC’s low footprint in a constrained environment, you can opt back into it explicitly:

java -XX:+UseSerialGC -jar your-application.jar

Java 26 and Earlier vs. Java 27

Environment Java 26 and Earlier Java 27
1 CPU or < 1,792 MB memory Serial GC (automatic) G1 GC (default)
Multi-CPU server G1 GC (default) G1 GC (default)
Explicit override -XX:+UseG1GC needed in constrained envs -XX:+UseSerialGC needed to opt out

JEP 534: Compact Object Headers by Default

Every object on the 64-bit HotSpot JVM has carried a 96-bit (12-byte) header since forever: a mark word plus a class pointer. Project Lilliput has been chipping away at that for several releases: experimental in Java 24 (JEP 450), opt-in and production-ready in Java 25 (JEP 519), and now, with JEP 534, switched on by default in Java 27.

java -XX:+PrintFlagsFinal -version | grep -i CompactObjectHeaders

Output:

bool UseCompactObjectHeaders = true {product lp64_product} {default}

Reported gains from Oracle’s own SPECjbb2015 benchmarking: roughly 22% less heap space and 8% less CPU time, with about 15% fewer garbage collection cycles, simply from smaller headers packing more live objects per region. If you need the old 96-bit layout for compatibility reasons, you can still disable it:

java -XX:-UseCompactObjectHeaders -jar your-application.jar

There is no application code to change here. It is exactly the kind of feature that helps you without you noticing, until you check your container’s memory graphs after the upgrade.

JEP 527: Post-Quantum Hybrid Key Exchange for TLS 1.3

This is arguably the headline feature of Java 27. TLS 1.3 connections that rely only on classical key exchange algorithms are vulnerable to a “harvest now, decrypt later” attack: an adversary records encrypted traffic today and waits for a sufficiently powerful quantum computer to decrypt it later. JEP 527 adds three hybrid key exchange schemes to the SunJSSE provider, each combining a quantum-resistant ML-KEM algorithm with a traditional elliptic-curve algorithm, so the connection stays secure as long as at least one of the two algorithms remains unbroken.

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import java.util.Arrays;

public class PostQuantumTlsDemo {
    public static void main(String[] args) throws Exception {
        SSLSocket socket = (SSLSocket) SSLContext.getDefault()
                .getSocketFactory()
                .createSocket();

        SSLParameters params = socket.getSSLParameters();
        System.out.println("Default named groups: " + Arrays.toString(params.getNamedGroups()));

        // Explicitly prefer two hybrid post-quantum groups, then fall back to classical ones
        params.setNamedGroups(new String[] {
                "SecP256r1MLKEM768", "X25519MLKEM768", "secp256r1", "x25519"
        });
        socket.setSSLParameters(params);
    }
}

Illustrative output:

Default named groups: [X25519MLKEM768, x25519, secp256r1, secp384r1, secp521r1, x448, ffdhe2048, ffdhe3072, ffdhe4096]

Notice that X25519MLKEM768, the hybrid scheme, is already first in the default list. Applications using the standard javax.net.ssl API get this protection automatically, with zero code changes, as long as they have not already hard-coded a specific set of named groups. You can also control the default list JVM-wide with the jdk.tls.namedGroups system property instead of touching code.

JEP 536: JFR In-Process Data Redaction

Java Flight Recorder is a favorite tool for diagnosing production issues, but recordings capture command-line arguments, environment variables, and system properties verbatim, including any passwords, API keys, or tokens that happen to be passed that way. Once a .jfr file is shared with a support team or attached to a bug report, that secret is out. JEP 536 redacts this data in-process, before it is ever written to the recording.

# Start a recording on a service launched with a password on the command line
java -XX:StartFlightRecording:filename=recording.jfr -jar payment-service.jar --password=Sup3rSecret

# The default filters redact it automatically before anything hits recording.jfr
jfr print --events InitialSystemProperty,JVMInformation recording.jfr

Output (default redaction filters applied):

jdk.JVMInformation {
  javaArguments = "-jar payment-service.jar [REDACTED]"
}
You can extend the default filters with your own application-specific keywords, or load them from a file to avoid unwieldy command lines:
java -XX:FlightRecorderOptions:'redact-key=+confidential;secret;@keys.txt' -jar app.jar

And if you genuinely need the old, unredacted behavior for a specific diagnostic session:

java -XX:FlightRecorderOptions:'redact-key=none,redact-argument=none' -jar app.jar

JEP 538: PEM Encodings of Cryptographic Objects (Third Preview)

The PEMEncoder/PEMDecoder API you may have used in Java 26 (JEP 524) goes through a third preview round here. Interestingly, this JEP was originally expected to finalize in Java 27 after two preview rounds, but late feedback from the community led the team to ship one more preview instead, a reminder that “third preview” does not always mean “smooth, incremental polish”; sometimes it means real design questions surfaced late.

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PEMEncoder;
import java.security.PEMDecoder;
import java.security.PrivateKey;

public class PemEncodingDemo {
    public static void main(String[] args) throws Exception {
        KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
        KeyPair keyPair = generator.generateKeyPair();

        String pemText = PEMEncoder.of().encodeToString(keyPair.getPrivate());
        System.out.println(pemText);

        PrivateKey decodedKey = PEMDecoder.of().decode(pemText, PrivateKey.class);
        System.out.println("Decoded key algorithm: " + decodedKey.getAlgorithm());
    }
}

Sample output:

-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
-----END PRIVATE KEY-----
Decoded key algorithm: EC

If your team is still on Java 26 and used PEMEncoder/PEMDecoder there, this code keeps working unchanged; the third preview mostly refines edge cases rather than the everyday encode/decode path shown above.

JEP 537: Vector API (Twelfth Incubator)

The Vector API reaches its twelfth incubation round, with no substantial implementation changes since Java 25. It still lives in the jdk.incubator.vector module, expressing SIMD-style vector computations that the JIT compiles down to native vector instructions where the CPU supports them.

import jdk.incubator.vector.DoubleVector;
import jdk.incubator.vector.VectorSpecies;

public class VectorApiDemo {
    static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_256;

    public static void main(String[] args) {
        double[] prices = {19.99, 29.99, 9.99, 49.99};
        double[] discounted = new double[prices.length];

        DoubleVector priceVector = DoubleVector.fromArray(SPECIES, prices, 0);
        priceVector.mul(0.9).intoArray(discounted, 0);

        System.out.println(java.util.Arrays.toString(discounted));
    }
}

Output:

[17.991, 26.991, 8.991, 44.991]

Compile and run it with the incubator module explicitly on the module path:

javac --add-modules jdk.incubator.vector VectorApiDemo.java 
java --add-modules jdk.incubator.vector VectorApiDemo

Per the JEP itself, the Vector API will keep incubating until Project Valhalla’s value types are far enough along to let it graduate from incubation into an actual preview feature.

How Do You Enable Preview Features in Java 27?

Four JEPs here (531, 532, 533, 538) are previews, and one (537) is an incubator module. Both categories need explicit flags on the command line:

Feature type Compile Run
Preview (531, 532, 533, 538) javac –release 27 –enable-preview Main.java java –enable-preview Main
Single-file source launcher Not needed java –enable-preview Main.java
Incubator (537, Vector API) javac –add-modules jdk.incubator.vector Main.java java –add-modules jdk.incubator.vector Main

As always, avoid building production code paths around preview APIs. The removal of isInitialized()/orElse() from Lazy Constants between Java 26 and Java 27 is a good live example of exactly why: preview APIs can and do change shape between releases.

Common Mistakes to Avoid

  • Assuming Compact Object Headers or the G1-everywhere change need a flag to take effect. Both are on by default in Java 27; you would need a flag to turn them off, not on.
  • Skipping a staging measurement after a “routine” base image bump to jdk:27. Two default JVM behaviors changed (GC selection and object header size), even though no application code did.
  • Hard-coding a specific list of TLS named groups and then wondering why post-quantum key exchange never kicks in. JEP 527 only helps automatically if your code has not already overridden the default group list.
  • Treating JEP 538’s “third preview” as a formality. It exists specifically because the API was not ready to finalize, so expect at least one more preview round before PEM encoding is locked in.
  • Forgetting –enable-preview on both javac and java for JEP 531, 532, 533, and 538 code.

Frequently Asked Questions (FAQs)

Q#1: Is Java 27 a long-term support (LTS) release?

No. Java 27 is a short-term, non-LTS release supported for six months. Java 25 remains the current LTS version, and the next LTS is expected to be Java 29 around September 2027.

Q#2: What are the biggest new features in Java 27?

The four finalized JEPs stand out: G1 as the default garbage collector in all environments (JEP 523), Compact Object Headers on by default (JEP 534), post-quantum hybrid TLS 1.3 key exchange (JEP 527), and JFR in-process data redaction (JEP 536).

Q#3: Do I need to change my code to get post-quantum TLS protection?

No. As long as your application uses the standard javax.net.ssl APIs and does not explicitly override the TLS named groups, the hybrid X25519MLKEM768 scheme is preferred automatically.

Q#4: Why did the PEM encoding API (JEP 538) get a third preview instead of finalizing?

It was originally planned to finalize in Java 27 after two preview rounds, but late community feedback led the OpenJDK team to ship one more preview round to address open design questions first.

Q#5: How do I enable preview features in Java 27?

Compile with javac –release 27 –enable-preview Main.java and run with java –enable-preview Main, or use the single-file source launcher directly with java –enable-preview Main.java.

Q#6: What changed in Structured Concurrency’s seventh preview?

StructuredTaskScope and Joiner gained a third type parameter that describes exactly which exception type join() can throw, giving callers more precise, compile-time-checked error handling.

Conclusion: Should You Upgrade to Java 27?

Java 27 is a smaller release than Java 26 in JEP count, but two of its finalized changes, Compact Object Headers and G1 everywhere, are exactly the kind of “free” improvements worth grabbing in any environment where you can move off Java 25 or 26 without an LTS commitment. JEP 527’s post-quantum TLS support is the standout for security-conscious teams, especially anyone with a compliance deadline around quantum readiness. Everything still in preview (Lazy Constants, primitive patterns, Structured Concurrency, PEM encoding) or incubation (the Vector API) is worth tracking now, since most of it is expected to stabilize by the time Java 29 arrives as the next LTS.

If you want to try these examples without setting up a local JDK 27 environment, use our free online Java compiler and code runner.

References:


For other version’s features, kindly go through Java Features After Java 8.

One thought on “Java 27 New Features With Examples”

Leave a Reply



Top