The Evolution of Java: Key Changes from Java 8 to Java 21
Java has been at the forefront of enterprise development for decades, evolving with each release to meet the demands of modern software engineering. From the introduction of lambdas in Java 8 to the revolutionary virtual threads in Java 21, the platform has consistently grown to provide developers with more powerful tools, simplified syntax, and improved performance.
In this article, we’ll take a journey through the most significant changes in each major LTS release:
Java 8 (2014):
- Lambda Expressions: Introduced functional programming capabilities.
- Stream API: Enabled efficient operations on collections using streams.
- Default Methods: Allowed interfaces to have methods with implementations.
- Optional Class: Provided a way to avoid
NullPointerExceptions
. - New Date/Time API: Introduced a more modern and immutable date/time API via the
java.time
package.
Java 11 (2018):
- Local-Variable Syntax for Lambda Parameters: Enabled using the
var
keyword in lambda expressions. - HTTP Client API: Introduced a new API for handling HTTP/2 and WebSockets.
- String Enhancements: New methods like
isBlank()
,lines()
,repeat()
, andstrip()
. - File Methods: Added convenient methods like
readString()
andwriteString()
for file operations. - Removal of Java EE and CORBA Modules: Removed outdated technologies to streamline the platform.
Java 17 (2021):
- Sealed Classes: Allowed restricted class hierarchies.
- Pattern Matching for
instanceof
: Simplifiedinstanceof
checks by integrating pattern matching. - Records: Enabled a concise syntax for immutable data classes.
- Text Blocks: Simplified the creation of multiline strings using triple quotes.
- Strong Encapsulation in Modules: Enhanced encapsulation of internal APIs, improving modularity.
Java 21 (2023):
- Pattern Matching for
switch
: Extendedswitch
statements with pattern-matching capabilities. - Scoped Values: Provided an alternative to thread-local variables with more control over value propagation.
- String Templates: Enabled dynamic string construction using embedded expressions inside string templates.
- Sequenced Collections: Added new collection types (
List
,Set
,Map
) that maintain consistent element order. - Virtual Threads: Introduced virtual threads to allow efficient and lightweight concurrency.
In the following sections, we’ll dive deeper into these innovations, exploring how they have transformed Java programming over time and continue to make it one of the most robust and scalable languages available today.
Java 8: Ushering in Functional Programming
Released in 2014, Java 8 was a game-changer for the platform, introducing functional programming concepts that significantly improved how developers worked with collections and other data structures. Some of the standout features include:
Lambda Expressions
Lambda expressions brought functional programming to Java, making code more concise and expressive. Instead of writing anonymous inner classes, we could now define inline implementations of functional interfaces.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));
Stream API
Java 8’s Stream API introduced a functional approach to processing collections, making it easier to work with large data sets in a declarative way.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream()
.filter(n -> n % 2 == 0)
.mapToInt(n -> n)
.sum();
System.out.println(sum); // Output: 6
New Date and Time API
The java.time
package in Java 8 provided a modern, immutable, and comprehensive approach to date and time handling, replacing the cumbersome java.util.Date
.
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plusWeeks(1);
System.out.println(nextWeek);
Optional Class
The Optional
class was introduced to help avoid NullPointerExceptions
this by providing a container for values that may or may not be present. It encourages developers to handle absent values explicitly.
Optional<String> name = Optional.ofNullable("Alice");
name.ifPresent(System.out::println); // Output: Alice
Optional<String> empty = Optional.empty();
System.out.println(empty.isPresent()); // Output: false
Default Methods in Interfaces
Java 8 introduced default methods in interfaces, allowing interfaces to have method implementations. This enables developers to add new methods to interfaces without breaking existing implementations.
interface Vehicle {
default void start() {
System.out.println("Vehicle is starting");
}
}
class Car implements Vehicle { }
public class Main {
public static void main(String[] args) {
Vehicle car = new Car();
car.start(); // Output: Vehicle is starting
}
}
Java 11: A Step Towards Simplicity
Java 11, released in 2018, built on the foundations of Java 8 by streamlining certain aspects of the language and adding features that enhanced developer productivity.
HTTP Client API
Java 11 introduced a new HttpClient
API, simplifying how we handle HTTP connections, including support for HTTP/2 and WebSockets.
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
String Enhancements
Java 11 brought several new methods to the String
class that simplified common string operations. Some of the most useful additions include:
isBlank()
: Checks if the string is empty or contains only whitespace.strip()
: Removes leading and trailing whitespaces, including Unicode whitespaces.lines()
: Returns a stream of lines from a multi-line string.repeat()
: Repeats the string a specified number of times.
String str = " Hello World! ";
System.out.println(str.isBlank()); // Output: false
System.out.println(str.strip()); // Output: "Hello World!"
System.out.println("Java".repeat(3)); // Output: JavaJavaJava
Local-Variable Syntax for Lambda Parameters
Java 11 introduced the ability to use the var
keyword in lambda expressions, which allowed for a more concise way to declare variable types in lambdas while maintaining readability.
var numbers = List.of(1, 2, 3);
numbers.forEach((var number) -> System.out.println(number));
File Methods (readString()
and writeString()
)
Java 11 simplified file handling by introducing methods such as Files.readString()
and Files.writeString()
, which allows developers to read from and write to files with minimal code.
Path filePath = Paths.get("example.txt");
Files.writeString(filePath, "Hello, World!"); // Write string to file
String content = Files.readString(filePath); // Read string from file
System.out.println(content); // Output: Hello, World!
Removal of Java EE and CORBA Modules
Java 11 removed several outdated modules, including Java EE (Enterprise Edition) and CORBA (Common Object Request Broker Architecture), which were no longer relevant in modern Java applications. This streamlining made Java lighter and easier to maintain, focusing on core language and library improvements.
Java 17: Building Robust Applications with Modern Syntax
Java 17, an LTS release from 2021, focused on making Java cleaner and more powerful with new syntactic sugar and improvements to data structures.
Sealed Classes
Sealed classes allow developers to restrict which classes can extend or implement a specific class or interface, enhancing the control over inheritance hierarchies.
public abstract sealed class Shape permits Circle, Rectangle { }
final class Circle extends Shape { }
final class Rectangle extends Shape { }
Pattern Matching for instanceof
Java 17 simplifies type checking and casting with pattern matching for instanceof
, reducing boilerplate code when working with different types.
Object obj = "Hello";
if (obj instanceof String str) {
System.out.println(str.toUpperCase()); // Output: HELLO
}
Records
Records are a new way to define simple data carrier classes, significantly reducing the boilerplate code needed for classes that only hold data.
public record Point(int x, int y) {}
Point point = new Point(10, 20);
System.out.println(point.x()); // Output: 10
Text Blocks
Java 17 officially incorporated text blocks introduced as a preview in Java 13. Text blocks simplify writing multiline strings using triple quotes (“”), making the code more readable.
String textBlock = """
Hello,
World!
This is a text block.
""";
System.out.println(textBlock);
Strong Encapsulation in Modules
Java 17 reinforced the module system by making internal APIs more strictly encapsulated. This ensures that only explicitly exported packages are accessible to other modules, improving security and modularity. Internal APIs that were previously accessible but not intended for public use are now strongly encapsulated, forcing developers to use well-defined APIs.
module mymodule {
exports com.mypackage; // Only exports public APIs
}
Java 21: The Future of Java is Here
Java 21, the latest LTS release (2023), marks a significant leap forward, particularly with concurrency and pattern-matching innovations.
Virtual Threads
With the introduction of virtual threads (part of Project Loom), Java 21 offers a new way to handle concurrency, drastically improving scalability and simplifying thread management.
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> System.out.println("Hello from a virtual thread!"));
}
Pattern Matching for switch
Java 21 enhances the switch
statement with pattern matching, allowing developers to match on types, making the control flow much more powerful.
Object obj = 123;
String result = switch (obj) {
case Integer i -> "Integer: " + i;
case String s -> "String: " + s;
default -> "Unknown";
};
System.out.println(result); // Output: Integer: 123
String Templates
Java 21 introduces string templates, enabling more expressive and dynamic string construction with embedded expressions.
int x = 5;
String template = STR."Value of x is \{x}";
System.out.println(template); // Output: Value of x is 5
Scoped Values
Scoped Values are introduced as an alternative to thread-local variables. They allow for better control over the scope and propagation of values across execution contexts, providing a more structured way of managing state in concurrent applications.
ScopedValue<String> USER = ScopedValue.newInstance();
ScopedValue.where(USER, "Alice").run(() -> {
System.out.println(USER.get()); // Output: Alice
});
Sequenced Collections
Java 21 adds new collection types — SequencedCollection
, SequencedSet
, and SequencedMap
—to ensure that collections maintain element order consistently across different implementations. This guarantees insertion order across List
, Set
, and Map
collections.
SequencedSet<String> names = SequencedSet.of("Alice", "Bob", "Charlie");
names.forEach(System.out::println); // Output: Alice, Bob, Charlie
System.out.println("First: " + names.getFirst()); // Output: Alice
System.out.println("Last: " + names.getLast()); // Output: David
// Reverse iteration
names.reversed().forEach(System.out::println); // Output: David, Charlie, Bob, Alice
SequencedMap<Integer, String> ageMap = SequencedMap.of(1, "One", 2, "Two", 3, "Three");
ageMap.put(4, "Four");
ageMap.forEach((key, value) -> System.out.println(key + ": " + value));
// Output: 1: One, 2: Two, 3: Three, 4: Four
System.out.println("First Entry: " + ageMap.getFirst()); // Output: 1=One
System.out.println("Last Entry: " + ageMap.getLast()); // Output: 4=Four
// Reverse iteration
ageMap.reversed().forEach((key, value) -> System.out.println(key + ": " + value));
Conclusion
The journey from Java 8 to Java 21 showcases a clear progression toward making Java more concise, expressive, and powerful without sacrificing its stability and backward compatibility principles. Whether it’s the functional programming introduced in Java 8, the pattern matching and records of Java 17, or the groundbreaking virtual threads of Java 21, Java continues to evolve to meet the needs of modern developers.
As Java progresses, it remains one of the most trusted platforms for building scalable, maintainable, high-performance applications. The future is bright for developers who embrace these changes, and Java continues to be a leading force in software engineering.