Java 16
Release Information
- Release Date: March 16, 2021
- Support Type: Non-LTS (6-month support)
- End of Support: September 2021 (superseded by Java 17 LTS)
- JEPs: 17 enhancements
Language Features
Pattern Matching for instanceof (JEP 394) - Standard
After two preview rounds, Pattern Matching became a standard feature.
Example:
// Concise type checking and casting
if (obj instanceof String s) {
System.out.println("Length: " + s.length());
}
// Works with negation
if (!(obj instanceof String s)) {
throw new IllegalArgumentException("Expected String");
}
// s not in scope here
// Combines with && operator
if (obj instanceof String s && s.length() > 10) {
System.out.println(s.toUpperCase());
}Real-world example:
public double getPerimeter(Shape shape) {
if (shape instanceof Circle c) {
return 2 * Math.PI * c.radius();
} else if (shape instanceof Rectangle r) {
return 2 * (r.width() + r.height());
} else if (shape instanceof Triangle t) {
return t.side1() + t.side2() + t.side3();
}
throw new IllegalArgumentException("Unknown shape");
}Benefits:
- Eliminates 90% of explicit casts in typical codebases
- Reduces boilerplate code
- Compile-time type safety
Records (JEP 395) - Standard
Records became a standard feature after two preview rounds.
Example:
public record Point(int x, int y) {}
// Auto-generated:
// - Constructor: Point(int x, int y)
// - Accessors: x(), y()
// - equals(), hashCode(), toString()Compact constructor:
public record Range(int min, int max) {
// Compact constructor for validation
public Range {
if (min > max) {
throw new IllegalArgumentException("min must be <= max");
}
}
}Record with custom methods:
public record Point(int x, int y) {
public double distanceFromOrigin() {
return Math.sqrt(x * x + y * y);
}
public Point translate(int dx, int dy) {
return new Point(x + dx, y + dy);
}
}Use cases:
- DTOs (Data Transfer Objects)
- API responses/requests
- Configuration classes
- Domain value objects
- Map entries, tuples
Sealed Classes (JEP 397) - Second Preview
Improvements from first preview:
- Narrowing primitive conversions in patterns
- Better integration with pattern matching
Example:
public sealed interface Expression
permits Value, Addition, Multiplication {
}
public record Value(int value) implements Expression {}
public record Addition(Expression left, Expression right) implements Expression {}
public record Multiplication(Expression left, Expression right) implements Expression {}
// Pattern matching with sealed types (preview)
public int eval(Expression expr) {
return switch (expr) {
case Value v -> v.value();
case Addition a -> eval(a.left()) + eval(a.right());
case Multiplication m -> eval(m.left()) * eval(m.right());
// No default needed - compiler knows all cases covered!
};
}Tool and API Features
Packaging Tool (JEP 392) - Standard
jpackage became a standard tool (was incubator in Java 14-15).
Create platform-specific installers:
# Windows installer
jpackage --input target/ \
--name MyApp \
--main-jar myapp.jar \
--main-class com.example.Main \
--type msi \
--icon myicon.ico \
--win-menu \
--win-shortcut
# macOS app bundle
jpackage --input target/ \
--name MyApp \
--main-jar myapp.jar \
--type dmg \
--icon myicon.icns
# Linux package
jpackage --input target/ \
--name myapp \
--main-jar myapp.jar \
--type debBenefits:
- Native installers with bundled JRE
- No separate Java installation required
- Professional deployment experience
- Code signing support
When to use:
- Desktop applications
- End-user software distribution
- Enterprise software deployment
Vector API (JEP 338) - Incubator
SIMD (Single Instruction, Multiple Data) operations for high-performance computing.
Example:
import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_PREFERRED;
void vectorComputation(float[] a, float[] b, float[] c) {
int i = 0;
int upperBound = SPECIES.loopBound(a.length);
for (; i < upperBound; i += SPECIES.length()) {
// Vectorized operations
FloatVector va = FloatVector.fromArray(SPECIES, a, i);
FloatVector vb = FloatVector.fromArray(SPECIES, b, i);
FloatVector vc = va.mul(vb);
vc.intoArray(c, i);
}
// Scalar tail
for (; i < a.length; i++) {
c[i] = a[i] * b[i];
}
}Performance: 4-8x faster than scalar code on modern CPUs
Use cases: Machine learning, scientific computing, image processing, cryptography
Incubator and Preview Features
Foreign Linker API (JEP 389) - Incubator
Call native libraries without JNI.
Example:
import jdk.incubator.foreign.*;
CLinker linker = CLinker.getInstance();
SymbolLookup stdlib = CLinker.systemLookup();
// Call strlen from C standard library
MethodHandle strlen = linker.downcallHandle(
stdlib.lookup("strlen").get(),
MethodType.methodType(long.class, MemoryAddress.class),
FunctionDescriptor.of(C_LONG, C_POINTER)
);
try (MemorySegment str = CLinker.toCString("Hello", ResourceScope.newConfinedScope())) {
long len = (long) strlen.invoke(str.address());
System.out.println("Length: " + len); // 5
}Benefits over JNI:
- 10x less code
- 4-5x better performance
- Type-safe API
- Better error messages
Foreign-Memory Access API (JEP 393) - Third Incubator
Safe and efficient off-heap memory access.
Example:
import jdk.incubator.foreign.*;
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(1024, scope);
// Write data
MemoryAccess.setIntAtOffset(segment, 0, 42);
MemoryAccess.setDoubleAtOffset(segment, 4, 3.14);
// Read data
int intValue = MemoryAccess.getIntAtOffset(segment, 0);
double doubleValue = MemoryAccess.getDoubleAtOffset(segment, 4);
}
// Memory automatically freed when scope closesGarbage Collection Improvements
ZGC: Concurrent Thread-Stack Processing (JEP 376)
Moved ZGC thread-stack processing from safepoints to concurrent phase.
Impact: Sub-millisecond pause times even more consistent
Performance gain: Reduces pause times by up to 50% in some workloads
Elastic Metaspace (JEP 387)
Returns unused class metadata (metaspace) memory to OS more promptly.
Before: Metaspace memory mostly retained until JVM shutdown
After: Unused metaspace returned to OS, reducing memory footprint
Benefit: Lower memory usage for applications with dynamic class loading
Impact: Particularly beneficial for:
- Containers with memory limits
- Applications using OSGi or plugin architectures
- Long-running applications with class churn
JVM and Runtime Improvements
Enable C++14 Language Features (JEP 347)
OpenJDK source code can now use C++14 features.
Impact: Internal improvement, enables better JDK implementation
User benefit: More maintainable JVM code, foundation for future features
Migrate from Mercurial to Git (JEP 357)
OpenJDK source code migrated from Mercurial to Git.
New repository: https://github.com/openjdk/
Impact: Better tooling, easier contributions, industry-standard workflow
Migrate to GitHub (JEP 369)
OpenJDK source code hosted on GitHub.
Benefits:
- Easier for contributors
- Better CI/CD integration
- Familiar platform for developers
Port to Alpine Linux (JEP 386) and Windows/AArch64 (JEP 388)
Alpine Linux: Lightweight Linux distribution using musl libc
Windows/AArch64: Support for ARM64 Windows (Microsoft Surface, AWS Graviton)
Benefit: Broader platform support, smaller Docker images with Alpine
Warnings and Encapsulation
Strongly Encapsulate JDK Internals (JEP 396)
Internal APIs (sun.*, com.sun.*) strongly encapsulated by default.
Breaking change: Code using internal APIs will fail
Migration: Use standard APIs or --illegal-access=permit temporarily
Example affected code:
// ❌ FAILS in Java 16+
import sun.misc.Unsafe;
Unsafe unsafe = Unsafe.getUnsafe();
// ✅ Use standard alternatives
// - java.lang.invoke.MethodHandles for reflection
// - java.lang.invoke.VarHandle for low-level operations
// - Foreign-Memory Access API for off-heap memoryCheck your code:
java --illegal-access=warn -jar myapp.jar
# Shows warnings for illegal accessMigration Considerations
Upgrading from Java 11 LTS:
- Internal API usage: Review warnings from
--illegal-access=warn - Records and Pattern Matching: Refactor verbose code to use new features
- Packaging: Consider jpackage for desktop app distribution
- Platform support: Take advantage of Alpine Linux for smaller containers
- GC: Evaluate ZGC improvements for low-latency requirements
Compatibility: Mostly binary compatible, but internal API encapsulation may break code
Related Topics
- Java 15 - Previous release
- Java 17 LTS - Next release (LTS)
- Records - By-example tutorial
- Pattern Matching - In-the-field guide
References
Sources:
- Java 16 Features (with Examples)
- Oracle Announces Java 16
- What is new in Java 16 - Mkyong.com
- New Features in Java 16 - Java Code Geeks
Official OpenJDK JEPs: