Java 16 Features

Java 16 (released March 2021) focused on polishing modern Java (records/pattern matching), improving performance, and adding practical new APIs. Below are the most blog-worthy features with copy-paste friendly code snippets.


1) Records (Finalized) — JEP 395

What it is:
Records give you a compact way to create immutable data carriers (like DTOs) with automatically generated constructor, getters, equals(), hashCode(), and toString().

Why it matters:
Perfect for DTOs, request/response objects, config models, and value types.

Example: Before vs Record

// Before (classic POJO)
public final class User {
    private final String name;
    private final int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() { return name; }
    public int getAge() { return age; }

    @Override public boolean equals(Object o) { /*...*/ return false; }
    @Override public int hashCode() { /*...*/ return 0; }
    @Override public String toString() { /*...*/ return ""; }
}
// Java 16: Record
public record User(String name, int age) { }

Custom validation inside a record (Compact Constructor)

public record Product(String id, double price) {
    public Product {
        if (id == null || id.isBlank()) throw new IllegalArgumentException("id required");
        if (price < 0) throw new IllegalArgumentException("price cannot be negative");
    }
}

Pro tip: Records are final and fields are implicitly private final.


2) Pattern Matching for instanceof (Finalized) — JEP 394

What it is:
No more manual cast after instanceof.

Example

Object obj = "TrueCode";

if (obj instanceof String s) {
    System.out.println(s.toUpperCase());
}

Old way (for comparison)

if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.toUpperCase());
}

3) Sealed Classes (Second Preview) — JEP 397

What it is:
You can restrict which classes are allowed to extend or implement a type.

Why it matters:
Great for domain modeling (especially in fintech / rule engines): you control the hierarchy.

Example

public sealed interface Payment permits CardPayment, UpiPayment { }

public final class CardPayment implements Payment { }
public final class UpiPayment implements Payment { }

Notes:

  • In Java 16 it was a preview feature (finalized later).
  • Using preview requires special compiler/runtime flags (shown near the end).

4) Vector API (Incubator) — JEP 338

What it is:
A new API to express vector computations that the JVM can optimize using SIMD instructions (where possible).

Why it matters:
High-performance numeric computing, image processing, ML preprocessing, signal processing, etc.

Small example (illustrative)

// Vector API is incubator in Java 16: module jdk.incubator.vector
import jdk.incubator.vector.IntVector;
import jdk.incubator.vector.VectorSpecies;

public class VectorDemo {
    static final VectorSpecies<Integer> SPECIES = IntVector.SPECIES_PREFERRED;

    public static int sum(int[] a) {
        int i = 0;
        IntVector acc = IntVector.zero(SPECIES);

        for (; i + SPECIES.length() <= a.length; i += SPECIES.length()) {
            var v = IntVector.fromArray(SPECIES, a, i);
            acc = acc.add(v);
        }

        int total = acc.reduceLanes(java.util.function.IntBinaryOperator::sum);

        for (; i < a.length; i++) total += a[i];
        return total;
    }
}

Tip: Keep this as an “advanced” section—most readers won’t use it daily, but it’s a strong feature highlight.


5) Strongly Encapsulate JDK Internals (Default) — JEP 396

What it is:
JDK internal APIs (like sun.misc.Unsafe and other internal packages) are more strongly encapsulated by default.

Why it matters:

  • Some older libraries that relied on internal JDK classes may break.
  • Encourages using supported APIs.

Practical guidance snippet for readers

If you see illegal access warnings or failures in legacy apps, look for:

  • outdated libraries
  • reflection hacks accessing jdk.internal.*
  • fixes: upgrade libs, or as a temporary workaround use --add-opens / --add-exports

(You can add a short note like this in your blog; it’s useful for real projects.)


6) Stream.toList() Convenience Method — JEP 338 (Actually added in Java 16)

What it is:
A clean terminal operation to collect into an unmodifiable list.

Example

import java.util.List;

List<String> names = List.of("susil", "rayaguru", "truecode")
        .stream()
        .map(String::toUpperCase)
        .toList();

System.out.println(names);

Important: The returned list is unmodifiable. If you need a mutable list:

var mutable = new java.util.ArrayList<>(names);

7) Packaging Tool jpackage (Finalized) — JEP 392

What it is:
Creates native installers/packages for Java apps:

  • Windows: .msi / .exe
  • macOS: .pkg / .dmg
  • Linux: .deb / .rpm

Example command

jpackage --name MyApp --input target \
         --main-jar myapp.jar \
         --type exe

Why it matters:
Makes Java desktop apps feel “native” and easier to distribute.


8) Performance & JVM Improvements (Quick Mention)

These are good “closing bullets” (no need to go deep unless your audience is JVM-heavy):

  • Elastic Metaspace: returns unused Metaspace memory to the OS more promptly (JEP 387).
  • ZGC improvements: better scalability/concurrency (JEP 376).

How to Compile/Run Preview & Incubator Features (Java 16)

If you include Sealed Classes or Vector API, add this practical section.

Sealed Classes (Preview)

javac --release 16 --enable-preview MyFile.java
java --enable-preview MyMain

Vector API (Incubator module)

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

Java 16 strengthened modern Java with records, finalized pattern matching for instanceof, added handy APIs like Stream.toList(), and continued evolving advanced capabilities like sealed classes (preview) and the Vector API (incubator). If you’re building clean domain models and DTOs in Java, Java 16 is a big readability and productivity win.