JDK 15 Key Features — Sealed Classes and ZGC Production Ready

Sealed Classes (Preview)

Sealed Classes (JEP 360) is a feature that explicitly restricts which classes can extend a given class. It introduces an “allow list” to Java’s inheritance system.

Using an apartment building access control analogy: a public class is like a building anyone can enter, a final class is a building with no entry allowed at all, and a Sealed class is a building only registered residents can enter.

// === Sealed class: permitted subclasses explicitly listed ===
sealed interface Shape permits Circle, Rectangle, Triangle {}

// final: cannot be extended further
record Circle(double radius) implements Shape {
    double area() {
        return Math.PI * radius * radius;
    }
}

// final: Record is implicitly final
record Rectangle(double width, double height) implements Shape {
    double area() {
        return width * height;
    }
}

// non-sealed: reopened — anyone can extend Triangle
non-sealed class Triangle implements Shape {
    private final double base;
    private final double height;

    Triangle(double base, double height) {
        this.base = base;
        this.height = height;
    }

    double area() {
        return 0.5 * base * height;
    }
}

public class SealedClassDemo {
    // Advantage of Sealed classes: since all subtypes are known,
    // the compiler can detect missing branches
    static String describe(Shape shape) {
        if (shape instanceof Circle c) {
            return "Circle(radius=" + c.radius() + ", area=" +
                   String.format("%.2f", c.area()) + ")";
        } else if (shape instanceof Rectangle r) {
            return "Rectangle(" + r.width() + "x" + r.height() + ", area=" +
                   String.format("%.2f", r.area()) + ")";
        } else if (shape instanceof Triangle t) {
            return "Triangle(area=" + String.format("%.2f", t.area()) + ")";
        }
        // Since it's Sealed, no other Shape implementations exist
        throw new AssertionError("Unknown shape");
    }

    public static void main(String[] args) {
        Shape circle = new Circle(5.0);
        Shape rect = new Rectangle(3.0, 4.0);
        Shape tri = new Triangle(6.0, 3.0);

        System.out.println(describe(circle));
        System.out.println(describe(rect));
        System.out.println(describe(tri));
        // Output:
        // Circle(radius=5.0, area=78.54)
        // Rectangle(3.0x4.0, area=12.00)
        // Triangle(area=9.00)
    }
}

Subclasses of a Sealed class must choose one of three modifiers:

KeywordMeaning
finalCompletely prevents further inheritance
sealedAlso specifies an allow list (extends the hierarchy)
non-sealedReopens for inheritance (free inheritance like regular classes)

Sealed Classes are finalized in Java 17 (LTS), and when combined with Pattern Matching for switch in Java 21, exhaustive switch (guaranteed coverage of all cases) becomes possible.

Text Blocks Finalized

After Java 13 (First Preview) and Java 14 (Second Preview), Text Blocks became a final feature in Java 15 (JEP 378). The --enable-preview flag is no longer needed.

Here’s a summary of the finalized Text Block features:

public class TextBlockFinal {
    public static void main(String[] args) {
        // === 1. Writing SQL queries ===
        String sql = """
                SELECT u.name, u.email, o.total
                FROM users u
                JOIN orders o ON u.id = o.user_id
                WHERE o.status = 'COMPLETED'
                  AND o.total > 10000
                ORDER BY o.total DESC
                """;
        System.out.println("SQL:");
        System.out.println(sql);

        // === 2. Combined with formatted() — dynamic value insertion ===
        String name = "John Doe";
        int minTotal = 50000;
        String dynamicSql = """
                SELECT * FROM orders
                WHERE customer_name = '%s'
                  AND total >= %d
                """.formatted(name, minTotal);
        System.out.println("Dynamic SQL:");
        System.out.println(dynamicSql);

        // === 3. Escape sequences ===
        // \s: preserve trailing whitespace (one space)
        // \(end of line): prevent line break
        String poem = """
                Spring comes\s\s\
                to mountains and fields\s\s\
                flowers bloom.
                """;
        System.out.println("Joined as one line:");
        System.out.println(poem);
        // Output: Spring comes  to mountains and fields  flowers bloom.

        // Output:
        // SQL:
        // SELECT u.name, u.email, o.total
        // FROM users u
        // JOIN orders o ON u.id = o.user_id
        // WHERE o.status = 'COMPLETED'
        //   AND o.total > 10000
        // ORDER BY o.total DESC
        //
        // Dynamic SQL:
        // SELECT * FROM orders
        // WHERE customer_name = 'John Doe'
        //   AND total >= 50000
        //
        // Joined as one line:
        // Spring comes  to mountains and fields  flowers bloom.
    }
}

Hidden Classes (JEP 371)

Hidden Classes is a feature for classes that frameworks and libraries generate at runtime. It’s rarely used directly in application code, but it’s an important change for the JVM ecosystem.

Key characteristics:

  • Cannot be referenced by name from other classes (undiscoverable)
  • Can be unloaded independently -> improved memory efficiency
  • Created via Lookup.defineHiddenClass()

They replace Unsafe.defineAnonymousClass() when frameworks dynamically generate classes for proxy patterns, Lambda metafactories, etc. Spring, Hibernate, and similar frameworks are transitioning internally.

ZGC Production Ready (JEP 377)

ZGC, which started experimentally in Java 11, has been promoted to production ready after 4 years. The -XX:+UnlockExperimentalVMOptions flag is no longer needed.

# Before Java 15: experimental flag required
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar app.jar

# Java 15 and later: use directly
java -XX:+UseZGC -jar app.jar

Here are ZGC’s production-ready performance numbers:

MetricG1 (default)ZGC
Max GC pause timeTens to hundreds of ms1-2ms
Average GC pause time~50msUnder 0.5ms
Supported heap sizeTens of GBUp to 16TB
Throughput overheadBaseline~5-15%

ZGC is suited for latency-sensitive services. It eliminates latency spikes caused by GC pauses in real-time trading systems, game servers, interactive web services, etc. For batch processing where throughput matters more, G1 may still be preferable.

Shenandoah GC Production Ready (JEP 379)

Shenandoah GC, led by Red Hat, was also simultaneously promoted to production ready. Like ZGC, it targets low latency but takes a different approach.

AttributeZGCShenandoah
OriginOracleRed Hat
Pause time1-2msUnder 10ms
Colored pointersUsedNot used (brooks pointers)
Max heap16TBSeveral TB

Since both GCs are production-ready, benchmarking with actual workloads before choosing is recommended.

# Enable Shenandoah
java -XX:+UseShenandoahGC -jar app.jar

# Compare performance with GC logs
java -XX:+UseZGC -Xlog:gc*:file=zgc.log -jar app.jar
java -XX:+UseShenandoahGC -Xlog:gc*:file=shenandoah.log -jar app.jar

Nashorn JavaScript Engine Removed (JEP 372)

The Nashorn JavaScript engine, introduced in Java 8, was removed. Java 11 issued deprecation warnings, and Java 15 completely removed it.

The alternative is GraalVM’s JavaScript engine. GraalVM is a runtime supporting Java as well as JavaScript, Python, Ruby, and other languages, with better performance than Nashorn.

If you have existing code using Nashorn via ScriptEngine, migration to GraalJS is required.

Summary

Here’s a comprehensive view of preview feature progression from Java 12 to 15:

FeatureJDK 12JDK 13JDK 14JDK 15Final
Switch ExpressionsPreviewImproved (yield)Final-JDK 14
Text Blocks-Preview2nd PreviewFinalJDK 15
Records--Preview2nd PreviewJDK 16
Pattern Matching (instanceof)--Preview2nd PreviewJDK 16
Sealed Classes---PreviewJDK 17

JDK 12-15 mark the transitional period where Java’s new development process of “preview -> feedback integration -> finalization” became established. Thanks to the 6-month release cycle and preview feature system, features refined through community feedback settled in gradually with high quality.

If you’re using Java 11 LTS in production, these features can all be enjoyed at once when upgrading to Java 17 LTS. Records, Pattern Matching, Sealed Classes, and Text Blocks — these four alone will noticeably reduce code volume.

Was this article helpful?