Level Up Your Coding: Essential Java 8 to 21 Features You Need to Master
Let’s dive into the journey from Java 8 to Java 21, focusing on key features introduced along the way, with detailed explanations and examples.
Java 8: The Foundation
1. Lambda Expressions
Lambdas brought functional programming to Java, letting you pass behavior as an argument. Reduces boilerplate code for implementing functional interfaces.
import java.util.Arrays;
import java.util.List;
public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println("Hello, " + name));
}
}
Hello, Alice
Hello, Bob
Hello, Charlie
name -> System.out.println(name)
is a lambda expression.
It is shorthand for implementing the Consumer
functional interface.
2. Streams API
Streams let you process collections declaratively — filtering, mapping, reducing — like a pipeline.
import java.util.Arrays;
import java.util.List;
public class StreamExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n * 2)
.sum();
System.out.println("Sum of doubled evens: " + sum);
}
}
3. Functional Interfaces and Default Methods
Interfaces with a single abstract method, enhanced with default
and static
methods.
@FunctionalInterface
interface Calculator {
int add(int a, int b);
default int subtract(int a, int b) {
return a - b;
}
}
@FunctionalInterface
ensures the interface has only one abstract method,
default
allows method implementation inside interfaces.
4. Optional Class
A container object that may or may not contain a non-null value.
Optional<String> name = Optional.ofNullable(null);
System.out.println(name.orElse("Default Name"));
Optional.ofNullable()
handles null
gracefully. orElse()
provides a fallback value.
5. New Date and Time API (java.time)
Replaced the old Date
and Calendar
APIs with more robust and immutable classes.
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plusDays(7);
System.out.println(nextWeek);
Java 9 (2017): Modularity and More
1. Module System (JEP 261)
Java 9 introduced modules for better encapsulation (via module-info.java). It’s less about code syntax and more about project structure.
// module-info.java
module com.example {
exports com.example.app;
}
// com/example/app/Main.java
package com.example.app;
public class Main {
public static void main(String[] args) {
System.out.println("Modular Java!");
}
}
2. Factory Methods for Collections
Simplified creation of immutable collections. List.of()
and Set.of()
create immutable collections.
List<String> fruits = List.of("Apple", "Banana", "Cherry");
Set<Integer> numbers = Set.of(1, 2, 3);
3. Enhanced try-with-resources
Improved resource management by supporting effectively final variables.
No need to declare the resource inside the try
block.
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
try (reader) {
System.out.println(reader.readLine());
}
Java 10 (Local Variable Type Inference)
Introduced the var
keyword for local variables.var
infers the data type at compile-time.
var message = "Hello, Java 10!";
System.out.println(message);
Java 11 (HTTP Client and String Enhancements)
Added a new HTTP client and enhanced String methods
String multiline = "Line1\nLine2\nLine3";
multiline.lines().forEach(System.out::println);
Java 14 (Switch Expressions)
Enhanced switch
with expressions and arrow labels.
int day = 2;
String dayType = switch (day) {
case 1, 7 -> "Weekend";
default -> "Weekday";
};
Java 21 (2023, LTS): Modern Java
Now, let’s hit the big Java 21 features, building on everything above.
1. Virtual Threads (JEP 444 — Final)
Unlike Java 8’s heavy platform threads, virtual threads are lightweight, perfect for concurrency.
public class ThreadExample {
public static void main(String[] args) throws InterruptedException {
// Java 8 platform thread
Thread thread8 = new Thread(() -> System.out.println("Platform thread"));
thread8.start();
thread8.join();
// Java 21 virtual thread
Thread thread21 = Thread.ofVirtual().start(() -> System.out.println("Virtual thread"));
thread21.join();
}
}
2. Pattern Matching for Switch (JEP 441 — Final)
This evolves Java 17’s switch, adding type patterns.
public class PatternSwitchExample {
public static void main(String[] args) {
Object obj = "Hello";
// Java 8
String result8;
if (obj instanceof String) {
result8 = "String: " + (String) obj;
} else {
result8 = "Not a string";
}
System.out.println(result8);
// Java 21
String result21 = switch (obj) {
case String s -> "String: " + s;
case Integer i -> "Integer: " + i;
default -> "Unknown";
};
System.out.println(result21);
}
}
3. Record Patterns (JEP 440 — Final)
Pairs with records (Java 16) to deconstruct them in patterns.
record Person(String name, int age) {}
public class RecordPatternExample {
public static void main(String[] args) {
Object obj = new Person("Alice", 25);
if (obj instanceof Person(String name, int age)) {
System.out.println(name + " is " + age);
}
}
}
4. String Templates (JEP 430 — Preview)
A cleaner way to build strings (enable with — enable-preview).
public class StringTemplateExample {
public static void main(String[] args) {
String name = "Bob";
int age = 30;
// Java 8
String result8 = name + " is " + age + " years old.";
System.out.println(result8);
// Java 21
String result21 = STR."\{name} is \{age} years old.";
System.out.println(result21);
}
}