Java Version Features (8 through 21)
In this tutorial, you will learn about Java Version Features (8 through 21). We cover key concepts, practical examples, and best practices to help you master this topic.
Java's Rapid Release Cadence
Since Java 9, Oracle adopted a six-month release cycle, delivering new features faster and more predictably. This changed Java from a language that stagnated between major releases (Java 6 to 7 took 5 years, 7 to 8 took 4 years) to one that evolves continuously. Understanding which version introduced which feature helps you leverage modern Java capabilities while maintaining compatibility with your target runtime.
This lesson surveys the most impactful language and API features from Java 8, the watershed release that transformed Java, through Java 21, the latest LTS release as of 2026.
timeline
title Java Version Timeline
2014 : Java 8 : Lambdas, Streams, Optional
2017 : Java 9 : Modules, JShell, HttpClient
2018 : Java 10 : var (LVTI)
2018 : Java 11 : LTS, HttpClient GA, Nest-Based Access
2019 : Java 12-13 : Switch Expressions (Preview)
2020 : Java 14-15 : Records, Text Blocks, Sealed Classes (Preview)
2021 : Java 17 : LTS, Sealed Classes GA, Pattern Matching
2023 : Java 21 : LTS, Virtual Threads, Record Patterns
Java 8 (March 2014)
Java 8 was a landmark release that introduced functional programming concepts to Java.
Lambda Expressions
// Before Java 8: anonymous inner class
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked");
}
});
// Java 8: lambda expression
button.addActionListener(e -> System.out.println("Clicked"));
Stream API
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
.filter(name -> name.startsWith("A"))
.map(String::toUpperCase)
.forEach(System.out::println); // ALICE
Optional
Optional<String> maybeName = Optional.ofNullable(getName());
String name = maybeName.orElse("Default");
Default and Static Methods in Interfaces
interface Vehicle {
void drive();
default void honk() {
System.out.println("Beep!");
}
static boolean isLegal() {
return true;
}
}
Java 9 (September 2017)
JPMS (Java Platform Module System)
Already covered in lesson 58. The module system was the headline feature.
JShell (REPL)
jshell> System.out.println("Hello, JShell!")
Hello, JShell!
Collection Factory Methods
List<String> list = List.of("a", "b", "c");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("key1", 1, "key2", 2);
Private Interface Methods
interface Logger {
default void log(String msg) {
log(msg, Level.INFO);
}
private void log(String msg, Level level) {
// Shared implementation
}
}
Java 10 (March 2018)
Local Variable Type Inference (var)
var list = new ArrayList<String>(); // infers ArrayList<String>
var stream = list.stream().filter(s -> !s.isEmpty());
var pair = Map.entry("key", "value");
var works only for local variables with initializers. It does not work for fields, method parameters, or return types.
Java 11 (September 2018, LTS)
HttpClient Standardization
var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com"))
.GET()
.build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Running Single-File Source Code
java HelloWorld.java
No compilation step needed for simple programs.
Nest-Based Access
Classes inside the same nest (typically a class and its inner classes) can access each other's private members without synthetic accessor methods.
class Outer {
private int x = 10;
class Inner {
void show() {
System.out.println(x); // No synthetic accessor needed in Java 11+
}
}
}
Java 14-15 (March/September 2020)
Records (Java 14 preview, 16 stable)
record Point(int x, int y) { }
var p = new Point(3, 4);
System.out.println(p.x()); // 3
System.out.println(p); // Point[x=3, y=4]
Text Blocks (Java 13 preview, 15 stable)
String json = """
{
"name": "Alice",
"age": 30,
"city": "New York"
}
""";
Pattern Matching for instanceof (Java 14 preview, 16 stable)
if (obj instanceof String s) {
System.out.println(s.length());
}
Java 17 (September 2021, LTS)
Sealed Classes
sealed interface Shape permits Circle, Rectangle, Triangle { }
final class Circle implements Shape { }
final class Rectangle implements Shape { }
final class Triangle implements Shape { }
Pattern Matching for switch (Preview)
String formatted = switch (obj) {
case Integer i -> "int: " + i;
case String s -> "str: " + s;
case null -> "null";
default -> "unknown";
};
Strong Encapsulation of JDK Internals
Reflection access to internal APIs is restricted by default. Use --add-opens to open specific packages.
Java 21 (September 2023, LTS)
Virtual Threads (Project Loom)
Covered in detail in lesson 57.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> System.out.println("Hello from VT"));
}
Record Patterns
record Address(String city, String zip) { }
if (obj instanceof Person(var name, var age, Address(var city, var zip))) {
System.out.println(name + " lives in " + city);
}
Pattern Matching for switch (Stable)
String result = switch (value) {
case null -> "null";
case String s when s.length() > 10 -> "long string";
case String s -> "short string";
case Integer i -> "number: " + i;
case int[] arr -> "array of length " + arr.length;
default -> "other";
};
Sequenced Collections
interface SequencedCollection<E> extends Collection<E> {
SequencedCollection<E> reversed();
void addFirst(E e);
void addLast(E e);
E getFirst();
E getLast();
E removeFirst();
E removeLast();
}
String Templates (Preview)
String name = "Alice";
String message = STR."Hello, \{name}!";
Feature Comparison Table
| Feature | Java Version | Status |
|---|---|---|
| Lambda Expressions | 8 | Stable |
| Stream API | 8 | Stable |
| Optional | 8 | Stable |
| JPMS (Modules) | 9 | Stable |
| var (LVTI) | 10 | Stable |
| HttpClient | 11 | Stable |
| Records | 16 | Stable |
| Pattern Matching (instanceof) | 16 | Stable |
| Sealed Classes | 17 | Stable |
| Virtual Threads | 21 | Stable |
| Pattern Matching (switch) | 21 | Stable |
| Record Patterns | 21 | Stable |
| Sequenced Collections | 21 | Stable |
| String Templates | 21+ | Preview |
| Scoped Values | 21+ | Preview |
Common Mistakes
1. Assuming Everyone Uses the Latest Java
Many enterprises are still on Java 11 or Java 17. Check your target runtime before using Java 21 features like virtual threads.
2. Overusing var
var can harm readability when the type is not obvious from the initializer.
var result = complexMethodCall(); // What type is result?
3. Forgetting That Records Are Immutable
Records are shallowly immutable. If a record component is a mutable object (like ArrayList), the record does not protect it.
4. Ignoring API Deprecation
APIs like finalize(), SecurityManager, and Thread.stop() have been deprecated for removal. Use their replacements.
5. Using Preview Features in Production
Preview features may change in future releases. Do not rely on preview features for production code unless you are willing to track changes.
6. Not Updating build.gradle or pom.xml
New features often require specific compiler versions and language level settings.
<!-- Maven: Java 21 -->
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
Practice Questions
- What is the difference between a preview feature and a stable feature?
- How does Java 21's
SequencedCollectionimprove upon standardCollection? - Why was
public static void mainchanged in Java 21 to allowvoid main()without parameters? - What are the advantages of
HttpClientover the legacyURLConnection? - How does pattern matching for switch improve code readability compared to traditional if-else chains?
Challenge: Rewrite a legacy Java 8 codebase that uses anonymous inner classes, manual null checks, and StringBuilder, to use Java 21 features including lambdas, Optional, text blocks, records, and pattern matching.
FAQ
Mini Project: Java Version Migration Tool
Create a utility that scans a source directory and identifies code patterns that can be modernized. For each file, report:
- Anonymous inner classes that can become lambdas
- Traditional for loops that can become stream operations
- Classes that can become records
- String concatenation that can become text blocks
- Null checks that can become Optional
- instanceof checks that can use pattern matching
Output a migration report as formatted text.
What's Next
Now that you have a broad view of Java's evolution, the next module explores enterprise Java. You will start with JDBC and Database Connectivity to learn how Java applications interact with relational databases.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro