Full Stack • Java • System Design • Cloud • AI Engineering

Java2026-06-08

Java 8 Features - Complete Guide

Comprehensive guide to Java 8 features including Lambda expressions, Stream API, Optional, Functional Interfaces, Method References, Default Methods, and Date/Time API with diagrams, data flows, and real-world examples.

Java 8 Features - Complete Guide

Java 8, released in March 2014, was one of the most significant updates to the Java programming language. It introduced functional programming capabilities, making Java code more concise, readable, and maintainable.

Why Java 8 Matters

  • Functional Programming: Lambda expressions and functional interfaces
  • Stream API: Declarative data processing
  • Better Code: More concise and readable
  • Performance: Parallel processing made easy
  • Modern Java: Foundation for Java 9+ features

Lambda Expressions

Lambda expressions enable functional programming in Java.

Syntax

(parameters) -> expression
(parameters) -> { statements; }

Before and After Java 8

// Before Java 8: Anonymous Class
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World");
    }
};

// Java 8: Lambda Expression
Runnable runnable = () -> System.out.println("Hello World");

Lambda Examples

import java.util.*;

public class LambdaExamples {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Venu", "John", "Alice", "Bob");
        
        // Sorting with lambda
        Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
        
        // forEach with lambda
        names.forEach(name -> System.out.println(name));
        
        // Thread with lambda
        new Thread(() -> System.out.println("Running")).start();
    }
}

Lambda Data Flow

Traditional:
┌─────────────┐     ┌──────────────┐
│ Anonymous   │ --> │ Implementation│
│   Class     │     │   (Verbose)   │
└─────────────┘     └──────────────┘

Lambda:
┌─────────────┐     ┌──────────────┐
│   Lambda    │ --> │ Implementation│
│  (Concise)  │     │   (Clean)     │
└─────────────┘     └──────────────┘

Functional Interfaces

Interface with exactly one abstract method.

Built-in Functional Interfaces

Interface Method Example
Predicate<T> test(T) x -> x > 0
Function<T,R> apply(T) x -> x * 2
Consumer<T> accept(T) x -> System.out.println(x)
Supplier<T> get() () -> new ArrayList<>()

Examples

import java.util.function.*;

public class FunctionalInterfaceDemo {
    public static void main(String[] args) {
        // Predicate - test condition
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println(isEven.test(4)); // true
        
        // Function - transform
        Function<String, Integer> length = s -> s.length();
        System.out.println(length.apply("Java")); // 4
        
        // Consumer - consume
        Consumer<String> print = s -> System.out.println(s);
        print.accept("Hello");
        
        // Supplier - supply
        Supplier<Double> random = () -> Math.random();
        System.out.println(random.get());
    }
}

Stream API

Functional approach to processing collections.

Stream Pipeline

┌──────────┐
│  Source  │ (Collection, Array)
└────┬─────┘
     │
     ▼
┌──────────┐
│Intermediate│ (filter, map, sorted)
│Operations│ (Lazy)
└────┬─────┘
     │
     ▼
┌──────────┐
│ Terminal │ (collect, forEach, reduce)
│Operation │ (Eager)
└──────────┘

Stream Operations

import java.util.*;
import java.util.stream.*;

public class StreamExamples {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // Filter even numbers
        List<Integer> evens = numbers.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
        System.out.println("Evens: " + evens);
        
        // Map to squares
        List<Integer> squares = numbers.stream()
            .map(n -> n * n)
            .collect(Collectors.toList());
        System.out.println("Squares: " + squares);
        
        // Sum using reduce
        int sum = numbers.stream()
            .reduce(0, (a, b) -> a + b);
        System.out.println("Sum: " + sum);
        
        // Count
        long count = numbers.stream()
            .filter(n -> n > 5)
            .count();
        System.out.println("Count > 5: " + count);
        
        // Sorted
        List<Integer> sorted = numbers.stream()
            .sorted(Comparator.reverseOrder())
            .collect(Collectors.toList());
        System.out.println("Sorted desc: " + sorted);
    }
}

Stream Processing Flow

Input: [1, 2, 3, 4, 5, 6]
   │
   ▼
filter(n%2==0) → [2, 4, 6]
   │
   ▼
map(n*n) → [4, 16, 36]
   │
   ▼
collect() → List[4, 16, 36]

Optional Class

Container to avoid NullPointerException.

Optional Methods

import java.util.Optional;

public class OptionalDemo {
    public static void main(String[] args) {
        // Create Optional
        Optional<String> optional = Optional.of("Hello");
        Optional<String> empty = Optional.empty();
        
        // Check if present
        if (optional.isPresent()) {
            System.out.println(optional.get());
        }
        
        // orElse - default value
        String value = empty.orElse("Default");
        System.out.println(value);
        
        // ifPresent - execute if present
        optional.ifPresent(s -> System.out.println(s));
        
        // map - transform
        Optional<Integer> length = optional.map(String::length);
        System.out.println("Length: " + length.get());
        
        // filter
        Optional<String> filtered = optional
            .filter(s -> s.startsWith("H"));
        System.out.println("Filtered: " + filtered.get());
    }
}

Optional Flow

┌─────────────┐
│ Optional.of │
└──────┬──────┘
       │
   ┌───┴───┐
   │Present│
   └───┬───┘
       │
   ┌───┴───┐
  Yes     No
   │       │
   ▼       ▼
┌─────┐ ┌────────┐
│get()│ │orElse()│
└─────┘ └────────┘

Method References

Shorthand for lambda expressions.

Types

1. Static:      ClassName::staticMethod
2. Instance:    instance::instanceMethod
3. Constructor: ClassName::new
4. Arbitrary:   ClassName::instanceMethod

Examples

import java.util.*;

public class MethodReferenceDemo {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Venu", "John", "Alice");
        
        // Lambda
        names.forEach(name -> System.out.println(name));
        
        // Method reference
        names.forEach(System.out::println);
        
        // Static method reference
        List<Integer> numbers = Arrays.asList(1, 2, 3);
        numbers.stream()
               .map(String::valueOf)
               .forEach(System.out::println);
        
        // Constructor reference
        Supplier<List<String>> listSupplier = ArrayList::new;
        List<String> list = listSupplier.get();
    }
}

Date and Time API

New API in java.time package.

Key Classes

LocalDate      - Date only
LocalTime      - Time only
LocalDateTime  - Date + Time
ZonedDateTime  - Date + Time + Zone
Instant        - Timestamp
Duration       - Time difference
Period         - Date difference

Examples

import java.time.*;
import java.time.format.DateTimeFormatter;

public class DateTimeDemo {
    public static void main(String[] args) {
        // LocalDate
        LocalDate today = LocalDate.now();
        LocalDate birthday = LocalDate.of(1990, 1, 15);
        System.out.println("Today: " + today);
        
        // LocalTime
        LocalTime now = LocalTime.now();
        LocalTime specific = LocalTime.of(14, 30);
        System.out.println("Now: " + now);
        
        // LocalDateTime
        LocalDateTime dateTime = LocalDateTime.now();
        System.out.println("DateTime: " + dateTime);
        
        // Formatting
        DateTimeFormatter formatter = 
            DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm");
        String formatted = dateTime.format(formatter);
        System.out.println("Formatted: " + formatted);
        
        // Period - date difference
        Period age = Period.between(birthday, today);
        System.out.println("Age: " + age.getYears() + " years");
        
        // Duration - time difference
        LocalTime start = LocalTime.of(9, 0);
        LocalTime end = LocalTime.of(17, 30);
        Duration duration = Duration.between(start, end);
        System.out.println("Hours: " + duration.toHours());
    }
}

Real-World Examples

Example 1: Employee Processing

import java.util.*;
import java.util.stream.*;

class Employee {
    private String name;
    private String department;
    private double salary;
    
    public Employee(String name, String dept, double salary) {
        this.name = name;
        this.department = dept;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }
}

public class EmployeeProcessing {
    public static void main(String[] args) {
        List<Employee> employees = Arrays.asList(
            new Employee("Venu", "Engineering", 150000),
            new Employee("John", "Finance", 120000),
            new Employee("Alice", "Engineering", 140000),
            new Employee("Bob", "Marketing", 110000)
        );
        
        // Find employees with salary > 120000
        List<Employee> highEarners = employees.stream()
            .filter(e -> e.getSalary() > 120000)
            .collect(Collectors.toList());
        
        System.out.println("High earners:");
        highEarners.forEach(e -> 
            System.out.println(e.getName() + ": " + e.getSalary()));
        
        // Group by department
        Map<String, List<Employee>> byDept = employees.stream()
            .collect(Collectors.groupingBy(Employee::getDepartment));
        
        System.out.println("\nBy Department:");
        byDept.forEach((dept, emps) -> {
            System.out.println(dept + ": " + emps.size() + " employees");
        });
        
        // Average salary by department
        Map<String, Double> avgSalary = employees.stream()
            .collect(Collectors.groupingBy(
                Employee::getDepartment,
                Collectors.averagingDouble(Employee::getSalary)
            ));
        
        System.out.println("\nAverage Salary:");
        avgSalary.forEach((dept, avg) -> 
            System.out.println(dept + ": $" + avg));
        
        // Total salary
        double total = employees.stream()
            .mapToDouble(Employee::getSalary)
            .sum();
        System.out.println("\nTotal Salary: $" + total);
    }
}

Example 2: Data Processing Pipeline

import java.util.*;
import java.util.stream.*;

public class DataPipeline {
    public static void main(String[] args) {
        List<String> data = Arrays.asList(
            "apple", "banana", "cherry", "date", 
            "elderberry", "fig", "grape"
        );
        
        // Complex pipeline
        String result = data.stream()
            .filter(s -> s.length() > 4)      // Filter long names
            .map(String::toUpperCase)          // Convert to uppercase
            .sorted()                          // Sort alphabetically
            .limit(3)                          // Take first 3
            .collect(Collectors.joining(", ")); // Join with comma
        
        System.out.println("Result: " + result);
        // Output: BANANA, CHERRY, ELDERBERRY
    }
}

Example 3: Parallel Processing

import java.util.*;
import java.util.stream.*;

public class ParallelProcessing {
    public static void main(String[] args) {
        List<Integer> numbers = IntStream.rangeClosed(1, 1000)
            .boxed()
            .collect(Collectors.toList());
        
        // Sequential
        long start = System.currentTimeMillis();
        long sum1 = numbers.stream()
            .mapToLong(Integer::longValue)
            .sum();
        long end = System.currentTimeMillis();
        System.out.println("Sequential: " + (end - start) + "ms");
        
        // Parallel
        start = System.currentTimeMillis();
        long sum2 = numbers.parallelStream()
            .mapToLong(Integer::longValue)
            .sum();
        end = System.currentTimeMillis();
        System.out.println("Parallel: " + (end - start) + "ms");
    }
}

Best Practices

1. Use Method References When Possible

// ❌ Less readable
list.forEach(item -> System.out.println(item));

// ✅ More readable
list.forEach(System.out::println);

2. Avoid Side Effects in Streams

// ❌ Bad - modifying external state
List<Integer> results = new ArrayList<>();
numbers.stream()
    .filter(n -> n > 5)
    .forEach(results::add);

// ✅ Good - use collect
List<Integer> results = numbers.stream()
    .filter(n -> n > 5)
    .collect(Collectors.toList());

3. Use Optional Properly

// ❌ Bad
Optional<String> opt = Optional.of("value");
if (opt.isPresent()) {
    String value = opt.get();
}

// ✅ Good
opt.ifPresent(value -> System.out.println(value));
// or
String value = opt.orElse("default");

4. Choose Right Stream Type

// For primitive types, use specialized streams
IntStream.range(1, 10)
    .sum();  // More efficient than Stream<Integer>

Summary

Java 8 introduced revolutionary features:

Lambda Expressions - Concise functional code ✅ Stream API - Declarative data processing ✅ Optional - Better null handling ✅ Method References - Cleaner code ✅ Default Methods - Interface evolution ✅ Date/Time API - Modern date handling

Key Benefits

  • Readability: More expressive code
  • Maintainability: Less boilerplate
  • Performance: Parallel processing
  • Safety: Better null handling

Next Steps

  1. Practice lambda expressions
  2. Master Stream API operations
  3. Use Optional in your code
  4. Explore parallel streams
  5. Learn advanced collectors

Remember: Java 8 is the foundation of modern Java. Master these features to write clean, efficient, and maintainable code! 🚀