Skip to content
Java java8 5 min read

Java 8 Features

Java 8 (released March 2014) was the most transformative release in Java’s history. It introduced functional programming idioms, a powerful data-processing pipeline, and a long-overdue replacement for the broken Date/Calendar API — all while staying fully backward-compatible.

If you are coming from Java 7 or earlier, this section is your most important stop. If you are already comfortable with Java 8, the deep-dive pages below will fill in the internals you may have skimmed past.

Java 8 lambda feature relationships

Why Java 8 Mattered

Before Java 8, writing even simple operations like “filter a list and collect results” required verbose anonymous inner classes and explicit loops. Java 8 changed the game with three foundational ideas:

  1. Lambdas — treat behavior as data, passing code as arguments just like you pass values.
  2. Streams — a declarative pipeline for processing collections, arrays, and I/O data.
  3. Functional interfaces — a contract that makes lambdas type-safe and composable.

Everything else in Java 8 builds on, or complements, these three pillars.

A Taste of the Difference

Here is the same task — filter even numbers and print them — written in both styles:

import java.util.Arrays;
import java.util.List;

public class BeforeAndAfter {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

        // Java 7 style
        for (int n : numbers) {
            if (n % 2 == 0) System.out.println(n);
        }

        // Java 8 style
        numbers.stream()
               .filter(n -> n % 2 == 0)
               .forEach(System.out::println);
    }
}

Output:

2
4
6
2
4
6

The Java 8 version reads almost like plain English, and scales to much more complex transformations without losing clarity.

Key Features at a Glance

FeaturePackage / TypeWhat it solves
Lambda ExpressionsLanguage syntaxReplaces verbose anonymous classes for single-method interfaces
Functional Interfacesjava.util.functionDefines the types that lambdas target (Function, Predicate, etc.)
Method ReferencesLanguage syntaxShorthand for lambdas that just call an existing method
Stream APIjava.util.streamDeclarative, lazy, parallelisable data pipelines
Optionaljava.util.OptionalEliminates NullPointerException by making absence explicit
Default MethodsLanguage / interfacesAdd new methods to interfaces without breaking existing code
forEach() on Iterablejava.lang.IterableCleaner iteration using a lambda or method reference
New Date/Time APIjava.timeImmutable, thread-safe replacement for Date and Calendar
Base64java.util.Base64Built-in encode/decode without third-party libraries
CompletableFuturejava.util.concurrentNon-blocking async programming (extends Future)
Nashorn JS Enginejavax.scriptEmbed JavaScript in Java (deprecated in Java 11)

Note: Java 8 is a Long-Term Support (LTS) release. Many production systems still run on Java 8, so mastering it is essential even if you also work with newer versions.

How Lambdas Connect to Everything Else

It helps to see these features as a dependency tree before diving in:

Lambda Expressions
    └─► Functional Interfaces  (give lambdas a type)
    └─► Method References      (shorthand syntax for lambdas)
    └─► Stream API             (built entirely on functional interfaces)
            └─► Collectors     (terminal operations for streams)
    └─► Default Methods        (enabled adding stream() to Collection)
    └─► forEach()              (iterates with a Consumer lambda)

Optional, Date/Time API, and Base64 are independent features that don’t require lambdas, though they pair naturally with streams.

Under the Hood

Lambdas and invokedynamic

Lambdas are not compiled to anonymous inner classes (a common misconception). The Java compiler emits an invokedynamic bytecode instruction, and at runtime the JVM’s LambdaMetafactory generates the actual implementation class on the fly using MethodHandles. This means:

  • The first call has a small one-time setup cost; subsequent calls are as fast as a direct method call.
  • No extra .class files are generated at compile time, keeping your JAR smaller.
  • The JIT compiler can inline lambdas aggressively, often making them zero-overhead compared to explicit loops.

Streams Are Lazy

A stream pipeline does not process any element until a terminal operation (like collect(), forEach(), reduce()) is called. Intermediate operations (filter(), map(), sorted()) build up a pipeline description. This laziness means short-circuiting operations like findFirst() stop processing as soon as they have an answer — no wasted work.

Default Methods and the Diamond Problem

Interfaces can now have concrete default methods. If a class implements two interfaces that both define the same default method, the compiler forces you to override it explicitly — Java resolves the ambiguity at compile time, not runtime, so there is no silent surprises.

java.time and Immutability

All core java.time classes (LocalDate, LocalDateTime, ZonedDateTime, etc.) are immutable and thread-safe. Every “modifier” method (like plusDays()) returns a new object rather than mutating the existing one — the same design as String. This makes date/time arithmetic safe to use in concurrent code without synchronization.

In This Section

  • Lambda Expressions — Write concise, anonymous functions with the -> syntax and understand how the JVM handles them.
  • Functional Interfaces — Explore Function, Predicate, Consumer, Supplier, and friends from java.util.function, and learn how to write your own.
  • Method References — Use :: to reference static methods, instance methods, or constructors as a cleaner alternative to single-method lambdas.
  • Stream API — Create and consume data pipelines over collections, arrays, and I/O with java.util.stream.Stream.
  • Stream Operations — Deep dive into filter(), map(), flatMap(), reduce(), and every other intermediate and terminal operation.
  • Collectors — Master Collectors.toList(), groupingBy(), joining(), partitioningBy(), and how to build custom collectors.
  • Optional — Wrap nullable return values safely, chain transformations with map()/flatMap(), and never write a bare null check again.
  • Default Methods — Add method implementations directly to interfaces for backward-compatible API evolution, and understand the resolution rules.
  • forEach() Method — Iterate any Iterable or Map with a lambda or method reference using the forEach() default method.
  • Date/Time API (java.time) — Work with LocalDate, LocalTime, ZonedDateTime, Duration, Period, and DateTimeFormatter in a clean, immutable API.
  • Base64 Encode/Decode — Encode and decode data to/from Base64 using the built-in java.util.Base64 class without any external dependency.

Quick Compatibility Notes

Java VersionStatus
Java 8Original release — all features on this page
Java 9+Streams gained takeWhile(), dropWhile(), iterate() overload
Java 10var usable in lambda parameters (Java 11)
Java 21All Java 8 APIs still present; CompletableFuture and streams heavily optimised

Tip: If you are starting a new project today, target Java 21 (the current LTS), but write code that avoids deprecated APIs. Everything you learn here carries forward with zero changes.

  • Lambda Expressions — the cornerstone feature that made Java 8 revolutionary.
  • Stream API — process collections declaratively and in parallel with elegant pipelines.
  • Functional Interfaces — the type system that makes lambdas and streams type-safe.
  • Modern Java (9–21) — explore what came after Java 8, from modules to records to virtual threads.
  • Collections Framework — the foundation that the Stream API is built on top of.
  • Optional — eliminate NullPointerException with a purpose-built wrapper type.
Last updated June 13, 2026
Was this helpful?