Java 8 is one of the most important releases in Java history. It introduced functional programming, stream processing, lambda expressions, and major API enhancements.
1. Lambda Expressions
What is the use of Lambda Expressions?
👉 To write cleaner, shorter, more readable code
👉 Makes Collections Processing Easy
Lambda expressions are used to write cleaner, shorter, and more readable code.
They allow passing behavior as a parameter and enable functional programming using the Stream API.
Lambdas reduce boilerplate, simplify multithreading, and work with functional interfaces like Predicate, Function, and Consumer. Default and static methods do not affect this rule.
We cannot use lambda expressions on normal interfaces that have multiple abstract methods.
Syntax:
(parameters) -> expression
(parameters) -> { statements }
What is the use of a Functional Interface?
2. Functional Interface
A Functional Interface is an interface with exactly has one abstract method.
To support lambda expressions: Lambda requires exactly one abstract method, so it can map the lambda to that method.
Functional interfaces allow lambdas, enable functional programming, reduce boilerplate, and allow behavior to be passed around in code.
Example:
@FunctionalInterface
interface MyInterface {
void test();
}
--
Examples:
Runnable
Callable
Comparator
Predicate
Function
Consumer
Supplier
What is the use of Default Methods?
A default method is a method with a body inside an interface.
Why are default methods introduced?
✔ 1. To avoid breaking existing implementations
Before Java 8: Adding a new method in interface → breaks ALL implementing classes
After Java 8:
✔ New method can be added as default
✔ Existing classes don't break
✔ To provide common functionality: Interfaces can now provide reusable code
What is the use of Static Methods in Interface?
Static methods inside interfaces belong only to the interface, not implementing classes.
Example:
interface Vehicle {
default void start() {
System.out.println("Starting...");
}
static void stop() {
System.out.println("Stopping...");
}
}
3. Method Reference
What is the use of Method Reference?
Method references are used to make lambda expressions more concise and readable.
They reuse existing methods instead of writing logic again in a lambda.
We can apply method references in four cases:
1. Static methods - Class::staticMethod,
2. Instance methods of an object - Class::instanceMethod,
3. Instance methods of any object of a class - object::instanceMethod,
4. Constructors - Class::new.
4. Stream API
What is a Stream in Java?
A Stream is pipeline-based data processing on a sequence of elements, and used to perform functional-style programming.
It allows to filter, transform, aggregate, and collect data without modifying the underlying source.
Key Characteristics:
👉Not a data structure — it doesn’t store elements
👉Immutable — operations do not change source data
👉Lazy evaluation — intermediate operations execute only when a terminal operation runs
👉Internal iteration — Java controls looping, not the developer
👉Functional — uses lambdas, method references
👉Composable — supports pipeline chaining
👉Single-use — once consumed, cannot be reused
Stream Pipeline Structure:
source → intermediate operations → terminal operation
Types of Stream Operations:
Intermediate (lazy, return Stream):
filter()
map()
sorted()
distinct()
limit()
peek()
Terminal (trigger execution):
collect()
forEach()
reduce()
count()
findFirst()
Example:
Find top 3 highest salaries using Stream
List<Integer> top3 = salaries.stream()
.sorted(Comparator.reverseOrder())
.limit(3)
.collect(Collectors.toList());
What is parallelStream()?
parallelStream() processes elements concurrently using multiple threads
internally backed by ForkJoinPool.commonPool().
Example:
List<String> names = List.of("A", "B", "C", "D");
names.parallelStream()
.forEach(System.out::println);
How Parallel Stream Works Internally:
✔ Uses ForkJoin Framework
✔ Splits the data into sub-tasks via Spliterator
✔ Each task runs on a different worker thread
✔ Results merged using reduce/combine
5. Optional
What is Optional in Java?
Optional is used to avoid null pointer exceptions and write safer, cleaner code.
It represents a value that may or may not exist.
Optional provides methods like of(), ofNullable(), isPresent(), ifPresent(), orElse(), orElseGet(), orElseThrow(), map(), flatMap(), and filter() to handle missing values without explicit null checks.
Example:
Optional<String> name = Optional.ofNullable(null);
name.orElse("default"); // returns "default"
name.orElseThrow(() -> new RuntimeException("Value missing"));
6. Date & Time API (java.time)
Core Conceptual Difference between util.Date and java.time
|
Aspect |
java.util.Date |
Java
8 Date/Time (`java.time.*`) |
|
Package |
java.util |
java.time |
|
Design |
Old, flawed |
Modern, well-designed |
|
Mutability |
|
|
|
Thread Safety |
|
|
|
Time representation |
Milliseconds since Epoch |
Diverse, explicit date/time models |
|
Time Zone handling |
Implicit, confusing |
Explicit, clear |
|
Formatting/Parsing |
`SimpleDateFormat` (not thread-safe) |
`DateTimeFormatter` (thread-safe) |
|
API style |
Procedural |
Fluent, functional |
|
Range & precision |
Milliseconds |
Nanoseconds |
|
Domain accuracy |
Weak |
Strong domain separation |
Date date = new Date(); // Sun Nov 23 13:22:39 IST 2025
date.setTime(0); // modifies original object
System.out.println(date); // given - Thu Jan 01 05:30:00 IST 1970
LocalDate localDate = LocalDate.now(); // 2025-11-23
localDate.plusDays(1); // returns new object
System.out.println(localDate); // same result - 2025-11-23
LocalDate update = localDate.plusDays(1); // return new object - 2025-11-23
Timezone Confusion:
java.util.Date always prints in system timezone
Java 8 separates concepts clearly:
LocalDate → only date
LocalTime → only time
LocalDateTime → date & time, no timezone
ZonedDateTime → date & time WITH timezone
Instant → UTC timestamp
Precision:
java.util.Date - gives till milliseconds
Instant, LocalDateTime - gives nanoseconds
Thread Safety:
Date, Calendar, SimpleDateFormat - Mutable
java.time classes are immutable
Calendar Exists Because Date Is Broken:
util.Date API did NOT support:
> adding days, months, years
> subtracting dates
> getting month, year, day
> localization
> timezone conversions
To over come this we use util.Calendar
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, 5);
Issue with Calendar: it consider months from 0-11
In java.time
LocalDate localDate = LocalDate.now();
localDate.plusDays(1);
Period, Duration Calculation:
java.util.Date and java.util.Calendar do NOT have dedicated time-span types.
Before Java 8, developers had to compute differences manually
In java8
Duration: Represents time-based amount
LocalTime start = LocalTime.of(10, 30);
LocalTime end = LocalTime.of(13, 10);
Duration duration = Duration.between(start, end);
Period: Represents date-based amount
LocalDate join = LocalDate.of(2020, 1, 15);
LocalDate today = LocalDate.of(2025, 11, 22);
Period period = Period.between(join, today);
No comments:
Post a Comment