-
Notifications
You must be signed in to change notification settings - Fork 0
Java 16
Release Date: March 16, 2021 | Type: Standard (non-LTS) | Status:
⚠️ End of Life
Java 16 graduates two of the most-loved previews — Records and Pattern Matching for instanceof — to standard features, and begins strongly encapsulating JDK internals by default, foreshadowing Java 17's stricter enforcement.
flowchart LR
A["Java 14: Records Preview"] --> B["Java 15: Records 2nd Preview"] --> C["Java 16: Records STANDARD ✅"]
D["Java 14: instanceof Preview"] --> E["Java 15: 2nd Preview"] --> F["Java 16: instanceof pattern matching STANDARD ✅"]
// Production-ready, no --enable-preview flag needed
public record Employee(String name, String department, double salary) {
// Compact constructor for validation
public Employee {
if (salary < 0) throw new IllegalArgumentException("Salary cannot be negative");
}
// Static factory method
public static Employee of(String name, String department, double salary) {
return new Employee(name, department, salary);
}
// Derived/computed method
public double annualSalary() {
return salary * 12;
}
}
// Records work great with Streams
List<Employee> employees = List.of(
new Employee("Alice", "Engineering", 9000),
new Employee("Bob", "Sales", 7000)
);
Map<String, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::department));Records implement interfaces too:
public interface Payable {
double salary();
}
public record Employee(String name, double salary) implements Payable {}public double calculateDiscount(Object customer) {
if (customer instanceof PremiumCustomer p && p.yearsActive() > 5) {
return 0.20;
} else if (customer instanceof PremiumCustomer) {
return 0.10;
} else if (customer instanceof RegularCustomer r) {
return r.hasCoupon() ? 0.05 : 0.0;
}
return 0.0;
}Scope rules — the pattern variable is only in scope where the compiler can prove the type check succeeded:
// Negated instanceof — variable is in scope AFTER the if, if it returns/throws
if (!(obj instanceof String s)) {
return;
}
System.out.println(s.length()); // ✅ s is in scope here — compiler proved obj is a StringWhat: A new API to express vector computations that reliably compile to optimal SIMD instructions on supported CPUs — huge for numerical/ML workloads.
import jdk.incubator.vector.*;
static void vectorAdd(float[] a, float[] b, float[] result) {
var species = FloatVector.SPECIES_PREFERRED;
int i = 0;
int upperBound = species.loopBound(a.length);
for (; i < upperBound; i += species.length()) {
var va = FloatVector.fromArray(species, a, i);
var vb = FloatVector.fromArray(species, b, i);
va.add(vb).intoArray(result, i);
}
// scalar tail loop for remaining elements
for (; i < a.length; i++) {
result[i] = a[i] + b[i];
}
}flowchart LR
A["Scalar loop\none add per cycle"] -->|Vector API| B["SIMD loop\n4-8+ adds per cycle"]
B --> C["2-8x speedup\non numeric workloads"]
What: --illegal-access=permit (the old default that let reflective access to internal JDK APIs slide with a warning) is now deny by default.
# Java 16+: internal API access now fails by default
java --illegal-access=permit ... # deprecated option, has no effect
# You must explicitly open modules if truly needed
java --add-opens java.base/java.lang=ALL-UNNAMED -jar app.jarWhy it matters: This is the direct predecessor to Java 17's full enforcement. If your app or a library dependency reflects into java.lang, java.util, etc., test on Java 16 now before you're forced to on 17.
var address = UnixDomainSocketAddress.of("/tmp/mysocket.sock");
try (var serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) {
serverChannel.bind(address);
// Faster local IPC than TCP loopback — no network stack overhead
}✅ Migrate DTOs/value objects to record now that it's standard — no preview flag risk
✅ Run your test suite on Java 16 with default settings to catch illegal-access issues before Java 17
✅ Consider the Vector API for hot numeric loops (image processing, ML preprocessing, physics) — but benchmark, it's still incubating
❌ Don't reach for Unix domain sockets unless you specifically need local-only IPC — TCP loopback is simpler and portable