Java 16 APIs & Features

Java 16 marked a significant update with Records and jpackage becoming standard features. It introduced support for Unix Domain Socket communication for inter-process communication. Elastic metaspace improved memory management, and the ZGC collector received further enhancements.

Released: March 2021
Standard Release

Java 16 Overview

Release Information

Release Date:

March 2021

Support Type:
Standard Release

Quick Summary

Java 16 marked a significant update with Records and jpackage becoming standard features. It introduced support for Unix Domain Socket communication for inter-process communication. Elastic metaspace improved memory management, and the ZGC collector received further enhancements.

Top Features

  • Records (Standard) - graduated from preview
  • Pattern Matching for instanceof (Standard)
  • jpackage tool (Standard) for creating installers
  • Unix Domain Socket Channels
  • Elastic Metaspace for better memory management
  • ZGC Concurrent Thread-Stack Processing

API Examples

Records (Standard)

Immutable data classes with minimal boilerplate

// Basic record
public record Point(int x, int y) {}

// Record with validation
public record Person(String name, int age) {
    public Person {
        if (name == null || name.isBlank()) {
            throw new IllegalArgumentException("Name cannot be null or blank");
        }
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
    
    // Additional methods
    public boolean isAdult() {
        return age >= 18;
    }
}

// Record with custom constructor
public record Temperature(double celsius) {
    public Temperature(double fahrenheit, boolean isFahrenheit) {
        this(isFahrenheit ? (fahrenheit - 32) * 5.0 / 9.0 : fahrenheit);
    }
    
    public double fahrenheit() {
        return celsius * 9.0 / 5.0 + 32;
    }
}

// Usage
var point = new Point(10, 20);
var person = new Person("John", 25);
var temp = new Temperature(100, true); // 100°F

System.out.println(point.x()); // 10
System.out.println(person.isAdult()); // true
System.out.println(temp.celsius()); // 37.78

Features of Records

Java Records provide a concise way to create immutable data classes with automatic generation of constructor, getters, equals(), hashCode(), and toString() methods. Records are perfect for modeling data transfer objects (DTOs) and value objects with minimal boilerplate code.

// 1. Basic Record Declaration
public record Point(int x, int y) {}

// 2. Record with Compact Constructor
public record Person(String name, int age) {
    public Person {
        if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
    }
}

// 3. Record with Custom Methods
public record Circle(double radius) {
    public double area() { return Math.PI * radius * radius; }
    public double circumference() { return 2 * Math.PI * radius; }
}

// 4. Record with Static Methods
public record Temperature(double celsius) {
    public static Temperature fromFahrenheit(double f) {
        return new Temperature((f - 32) * 5.0 / 9.0);
    }
    public double fahrenheit() { return celsius * 9.0 / 5.0 + 32; }
}

// 5. Record with Nested Records
public record Address(String street, String city) {}
public record Employee(String name, Address address) {}

// 6. Record with Generic Types
public record Pair<T, U>(T first, U second) {
    public static <T, U> Pair<T, U> of(T first, U second) {
        return new Pair<>(first, second);
    }
}

Unix Domain Socket Channels

Inter-process communication using Unix domain sockets

import java.nio.channels.SocketChannel;
import java.nio.channels.ServerSocketChannel;
import java.net.UnixDomainSocketAddress;
import java.net.StandardProtocolFamily;
import java.nio.ByteBuffer;
import java.nio.file.Path;

// Server side
Path socketPath = Path.of("/tmp/java-socket");
UnixDomainSocketAddress address = 
    UnixDomainSocketAddress.of(socketPath);

try (ServerSocketChannel serverChannel = ServerSocketChannel.open(
        StandardProtocolFamily.UNIX)) {
    
    serverChannel.bind(address);
    
    try (SocketChannel clientChannel = serverChannel.accept()) {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int bytesRead = clientChannel.read(buffer);
        
        // Process received data
        buffer.flip();
        String received = new String(buffer.array(), 0, bytesRead);
        System.out.println("Received: " + received);
    }
}

// Client side
try (SocketChannel clientChannel = SocketChannel.open(
        StandardProtocolFamily.UNIX)) {
    
    clientChannel.connect(address);
    
    String message = "Hello from client";
    ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
    clientChannel.write(buffer);
}