If you’ve been coding Java before version 8, you probably remember those days…
NullPointerException greeting us like an overly-friendly neighbour.Then came Java 8 — and everything changed.
Think of it as the moment when Java put on sunglasses, entered the world of functional programming, and said:
“Let’s write less and do more.”
This blog takes you through the coolest Java 8 features with real-world mini-scenarios, code samples, and reasons why they still matter today.
Grab a coffee… let’s start! ☕
Before Java 8, telling a Thread what to do felt like writing a mini novel.
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Processing files...");
}
}).start();
new Thread(() -> System.out.println("Processing files...")).start();
Real Use Case:
Imagine you’re building a batch job that runs several background tasks:
Lambda expressions let you define those actions in one line.
Why it matters:
More productivity
Cleaner APIs
Less “noise” in your codebase
A Functional Interface has one abstract method.
Example: Predicate, Function, Supplier.
Think of them as “contracts” for behaviors.
Scenario:
You run an HR portal and want to filter employees based on different rules — experience, salary, department, etc.
Instead of writing multiple classes for each rule, you simply pass logic using lambdas.
List<Employee> highEarners = employees.stream()
.filter(emp -> emp.getSalary() > 80000)
.toList();
You just converted logic into reusable behaviors. Boom.
Streams are like a conveyor belt where data flows, gets cleaned, sorted, filtered, and packaged.
You have 10,000 employees. You want to:
With Streams:
List<Employee> result = employees.stream()
.filter(e -> e.getExperience() > 3)
.sorted((a, b) -> Double.compare(b.getSalary(), a.getSalary()))
.limit(5)
.toList();
Bonus:
employees.parallelStream();
Java automatically uses multiple cores.
Before Java 8, adding a method to an interface broke implementations.
“Why not let interfaces evolve?”
interface PaymentGateway {
default void logTransaction() {
System.out.println("Transaction logged!");
}
}
java.lang.NullPointerException
Java 8’s Optional helps handle null safely.
String email = Optional.ofNullable(user.getEmail())
.orElse("Email not provided");
userOptional.ifPresent(u -> sendEmail(u.getEmail()));
Cleaner null handling, better APIs.
employees.forEach(System.out::println);
Instead of:
employees.forEach(e -> System.out.println(e));
Date was mutableJava 8 introduced modern APIs:
LocalDateLocalTimeLocalDateTimePeriodZoneIdLocalDate dob = LocalDate.of(1998, 3, 15);
LocalDate today = LocalDate.now();
int age = Period.between(dob, today).getYears();
System.out.println("Age: " + age);
Map<String, List<Employee>> byDept =
employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
Double avg = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
Map<String, List<Employee>> dashboard = employees.stream()
.filter(e -> e.getExperience() > 5)
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.sorted((a,b) -> Double.compare(b.getSalary(), a.getSalary()))
.limit(2)
.toList()
)
));
“Here, hold my stream.”
Even after newer versions, Java 8 remains widely used:
Used by enterprise systems
Default for Spring Boot
Streams & Lambdas everywhere
Core APIs still relevant
Java 8 is non-negotiable for serious developers.
Java 8 changed how Java is written:
Whether it's banking, e-commerce, or microservices — Java 8 is still at the core.