🔐 Java 17 Features (LTS)
A comprehensive guide to all major features added between Java 9 and Java 17 LTS, including Records, Sealed Classes, and Text Blocks.
1. Records (Java 16)
Records provide a compact syntax for declaring data-carrying classes. They automatically generate constructors, getters, equals(), hashCode(), and toString() methods.
public record PersonRecord(String name, int age) { // Compact constructor for validation public PersonRecord { if (age < 0) { throw new IllegalArgumentException("Age cannot be negative"); } } public static void main(String[] args) { PersonRecord p1 = new PersonRecord("Alice", 25); System.out.println(p1.name()); // Accessor is name(), not getName() System.out.println(p1); // PersonRecord[name=Alice, age=25] } }
interface Printable { void print(); } // Records can implement interfaces, but cannot extend classes public record PrintableItem(String id) implements Printable { @Override public void print() { System.out.println("Printing item: " + id); } }
2. Sealed Classes (Java 17)
Sealed classes allow you to restrict which classes can extend or implement them. This provides better control over the inheritance hierarchy.
// Sealed interface restricted to specific implementations public sealed interface Shape permits Circle, Rectangle, Triangle {} // Subclasses must be final, sealed, or non-sealed final class Circle implements Shape { double radius; } final class Rectangle implements Shape { double length, width; } non-sealed class Triangle implements Shape { double base, height; }
3. Text Blocks (Java 15)
Text blocks simplify the creation of multi-line strings, such as JSON, XML, or SQL queries, by eliminating the need for escape sequences and string concatenation.
public class TextBlockDemo { public static void main(String[] args) { // Multi-line JSON without escaping quotes String json = """ { "name": "Java", "version": 17, "lts": true } """; System.out.println(json); // SQL query spanning multiple lines String sql = """ SELECT id, name, email FROM users WHERE status = 'ACTIVE' ORDER BY created_at DESC """; } }
4. Pattern Matching for instanceof (Java 16)
This feature removes the need for explicit casting after an instanceof check by introducing pattern variables.
public class PatternMatchingDemo { public static void main(String[] args) { Object obj = "Hello Java 17"; // Old way if (obj instanceof String) { String s = (String) obj; System.out.println("Old Length: " + s.length()); } // New way (Pattern matching) if (obj instanceof String s) { // 's' is automatically casted and bound System.out.println("New Length: " + s.length()); } } }
5. Switch Expressions (Java 14)
Switch expressions modernize the traditional switch statement. They support returning values, arrow syntax, and prevent fall-through automatically.
public class SwitchExpressionDemo { public static void main(String[] args) { String day = "MONDAY"; // Switch expression returning a value String typeOfDay = switch (day) { case "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY" -> "Weekday"; case "SATURDAY", "SUNDAY" -> "Weekend"; default -> { System.out.println("Invalid day"); yield "Unknown"; // yield returns value from block } }; System.out.println(day + " is a " + typeOfDay); } }
6. Local Variable Type Inference (var) (Java 10)
The var keyword allows local variable declarations without explicitly stating the type. The compiler infers the type based on the initialization value.
import java.util.ArrayList; import java.util.HashMap; public class VarDemo { public static void main(String[] args) { // Reduces verbosity var list = new ArrayList<String>(); // Inferred as ArrayList<String> var map = new HashMap<Integer, String>(); // Inferred as HashMap<Integer, String> var message = "Hello Var"; // Inferred as String var number = 10; // Inferred as int for (var item : list) { System.out.println(item); } } }
7. Helpful NullPointerExceptions (Java 14)
Java 14+ provides detailed information in NPEs, pointing precisely to the object/variable that caused the exception.
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "name" is null
8. Collection Factory Methods (Java 9)
Creating immutable collections is now much simpler using factory methods like List.of(), Set.of(), and Map.of().
import java.util.List; import java.util.Map; import java.util.Set; public class CollectionFactoryDemo { public static void main(String[] args) { List<String> list = List.of("A", "B", "C"); Set<Integer> set = Set.of(1, 2, 3); Map<String, Integer> map = Map.of("One", 1, "Two", 2); System.out.println(list); } }
9. Private Interface Methods (Java 9)
Interfaces can now contain private methods to share code between default methods without exposing them to implementations.
interface Logger { default void logInfo(String message) { log("INFO: " + message); } default void logError(String message) { log("ERROR: " + message); } // Private helper method private void log(String message) { System.out.println("[Log System] " + message); } }
10. Java Module System (Java 9)
The Java Platform Module System (JPMS) introduced strong encapsulation and better modularity for large applications using module-info.java.
11. Enhanced Pseudo-Random Number Generators (Java 17)
Java 17 provides new interfaces and implementations for PRNGs.
import java.util.random.RandomGeneratorFactory; public class RandomDemo { public static void main(String[] args) { var generator = RandomGeneratorFactory.of("L128X256MixRandom").create(); System.out.println(generator.nextInt(100)); } }
12. Feature Timeline Table
| Version | Major Features Introduced |
|---|---|
| Java 9 | Module System, JShell, Collection Factories, Private Interface Methods |
| Java 10 | Local-Variable Type Inference (var) |
| Java 11 (LTS) | String Methods, HTTP Client API, var in Lambdas |
| Java 14 | Switch Expressions, Helpful NPEs |
| Java 15 | Text Blocks |
| Java 16 | Records, Pattern Matching for instanceof |
| Java 17 (LTS) | Sealed Classes, Enhanced PRNGs |