Java 8, released in March 2014, introduced a significant number of new features and enhancements aimed at improving productivity, performance, security, and ease of use. Here are some of the most notable features:
1. Lambda Expressions:
Explanation: A Lambda Expression is a short, anonymous function introduced in Java 8 that enables functional programming in Java. It allows you to write inline implementations of functional interfaces (interfaces with a single abstract method), making your code shorter, cleaner, and more expressive..
Example1:
// Traditional way
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world one!");
}
};
// Using lambda expression
Runnable r2 = () -> System.out.println("Hello world two!");
Example2:
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(element -> System.out.println(element));
Example3:
import java.util.stream.IntStream;
public class LambdaPrintExample {
public static void main(String[] args) {
IntStream.rangeClosed(1, 100)
.forEach(n -> System.out.println(n));
(OR)
List<Integer> numbers = IntStream.rangeClosed(1, 100)
.boxed() // Convert IntStream to Stream<Integer>
.collect(Collectors.toList());
// Print each number using a lambda
numbers.forEach(n -> System.out.println(n));
}
}
🧠Summary
| Feature | Lambda Expression Example |
|---|
| No arguments | () -> System.out.println("Hi") |
| One argument | x -> x * x |
| Multiple arguments | (a, b) -> a + b |
| With block | (a, b) -> { return a * b; } |
2.Functional Interfaces:
Explanation: A functional interface is an interface that contains only one abstract method, annotated with @FunctionalInterface. It can have multiple default or static methods. They can be used with lambda expressions and method references.
Example
✅ Example1: Custom Functional Interface
✅ Common Built-in Functional Interfaces (in java.util.function)
| Interface | Method | Use Case | Lambda Example |
|---|
Predicate<T> | test(T) | Returns boolean | x -> x > 10 |
Function<T, R> | apply(T) | Converts type T to type R | s -> s.length() |
Consumer<T> | accept(T) | Takes a value and returns nothing | x -> System.out.println(x) |
Supplier<T> | get() | Provides a value | () -> "Hello" |
UnaryOperator<T> | apply(T) | Operates on a single type T | x -> x * x |
BinaryOperator<T> | apply(T, T) | Operates on two values of same type | (a, b) -> a + b |
✅ Examples:
🔹 Predicate<T>:
🔹 Function<T, R>:
🔹 Consumer<T>:
🔹 Supplier<T>:
✅ Why Use Functional Interfaces?
-
Enable cleaner, more expressive code using lambdas.
-
Power the Stream API, event handlers, concurrency utilities, etc.
-
Provide building blocks for custom behavior injection.
3.Stream API:
Explanation: The Stream API was introduced in Java 8. It is mainly used when we want to process objects from a collection in a functional style. Streams support operations like map, filter, reduce, and more.
Example1:
List<String> myList = Arrays.asList("a1", "a2", "b1", "c2", "c1");
myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
Example2:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> squares = numbers
.stream()
.map(n -> n * n)
.collect(Collectors.toList());
4.Default Methods:
Explanation: Default methods allow you to add new methods to interfaces without breaking the existing implementations. They provide a default implementation that can be overridden by implementing classes.
Example:
interface MyInterface {
default void newMethod() {
System.out.println("Newly added default method");
}
void existingMethod();
}
5.Optional Class:
Explanation: The Optional class in Java is a container object introduced in Java 8 to avoid NullPointerException by representing the presence or absence of a value in a type-safe way.
✅ Why use Optional?
Traditionally:
With Optional:
✅ Creating Optional Instances:
6.Method References:
Explanation: Method references provide a way to refer to methods without executing them. They are a shorthand for lambda expressions that only call an existing method.
Example
// Using lambda expression
List<String> names = Arrays.asList("Swamy", "Java", "Spring");
list.forEach(item -> System.out.println(item));
// Using method reference
list.forEach(System.out::println);
7.Date and Time API:
Explanation: Java 8 introduced a new Date and Time API (java.time package) that is more comprehensive model for date and time that is more readable and less error-prone than the previous java.util.Date and java.util.Calendar classes.
✅ Key Features of java.time
-
Immutable and thread-safe
-
Fluent and easy-to-read API
-
Clear separation of date, time, date-time, instant, and zone
-
Better time zone support
-
Built-in parsing and formatting
-
Inspired by the Joda-Time library
Example
LocalDate date = LocalDate.now(); // e.g. 2025-06-19
LocalTime time = LocalTime.now(); // e.g. 09:41:32.123
LocalDateTime dateTime = LocalDateTime.now(); // e.g. 2025-06-19T09:41:32.123
8. Nashorn JavaScript Engine
Explanation: A JavaScript engine for executing embedded JavaScript code within Java applications. Provides improved performance and compliance with ECMAScript standards.
Example
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.eval("print('Hello, World!');");
9. Base64 Encoding and Decoding
Explanation: Java 8 introduced the java.util.Base64 class for encoding and decoding data in Base64 format.
Example
String originalInput = "test input";
String encodedString = Base64.getEncoder().encodeToString(originalInput.getBytes());
byte[] decodedBytes = Base64.getDecoder().decode(encodedString);
String decodedString = new String(decodedBytes);
10. Parallel Array Sorting
Explanation: Java 8 introduced the Arrays.parallelSort() method, which uses the Fork/Join framework to sort arrays in parallel.
Example
int[] array = {5, 3, 8, 1, 2};
Arrays.parallelSort(array);
11. Stamps and Counters
Explanation: New classes introduced for concurrent programming, such as StampedLock and LongAdder.
Example:
StampedLock lock = new StampedLock();
long stamp = lock.writeLock();
try {
// critical section
} finally {
lock.unlockWrite(stamp);
}
12. Concurrent Adders
Explanation: LongAdder and DoubleAdder are new classes that provide better performance in highly-concurrent scenarios.
Example
LongAdder adder = new LongAdder();
adder.increment();
System.out.println(adder.sum());
13. PermGen Removal
- Explanation: The permanent generation (PermGen) space was removed and replaced with Metaspace, which automatically grows as needed.
- Impact: Reduces OutOfMemoryError related to PermGen space and improves performance.
14. JavaFX Enhancements
- Explanation: JavaFX was enhanced with new UI controls, 3D graphics features, and new packaging tools.
- Example:
// Example code for JavaFX application
public class MyJavaFXApp extends Application {
@Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(event -> System.out.println("Hello World!"));
StackPane root = new StackPane();
root.getChildren().add(btn);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
15. Repeating Annotations
- Explanation: Java 8 allows the same annotation to be used multiple times on the same declaration or type use.
- Example:
@Repeatable(Schedules.class)
@interface Schedule {
String dayOfWeek();
String hour();
}
@Schedule(dayOfWeek = "Monday", hour = "10:00")
@Schedule(dayOfWeek = "Tuesday", hour = "11:00")
public void scheduledTask() {
// Task code
}
16. Type Annotations
Explanation: Java 8 allows annotations to be used in more places than before, such as on any type use.
Example
public class MyClass<@NonNull T> {
// Code
}
17. Improved Collections API
Explanation: The Collections API was improved with methods like removeIf(), forEach(), replaceAll(), and sort().
Example:
List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
list.removeIf(s -> s.startsWith("t"));
list.forEach(System.out::println);
------------
✅ 1) Predefined Functional Interfaces (Java 8)
Java 8 introduced several predefined functional interfaces in the java.util.function package for functional programming, especially useful with lambda expressions.
| Interface | Description | Signature |
|---|
| Predicate<T> | Tests a condition and returns boolean | boolean test(T t) |
| Function<T, R> | Transforms input T to output R | R apply(T t) |
| Consumer<T> | Consumes an input, returns nothing | void accept(T t) |
| Supplier<T> | Supplies a value of type T | T get() |
| UnaryOperator<T> | Takes T, returns T (same type) | T apply(T t) |
| BinaryOperator<T> | Takes two T’s, returns T | T apply(T t1, T t2) |
| BiFunction<T, U, R> | Two inputs, one output | R apply(T t, U u) |
| BiConsumer<T, U> | Two inputs, returns nothing | void accept(T t, U u) |
🔸 Example:
✅ 2) @FunctionalInterface
@FunctionalInterface is an annotation used to indicate that an interface is intended to be a functional interface—an interface with only one abstract method.
🔸 Purpose:
🔸 Features:
🔸 Example:
If a second abstract method is added, the compiler throws an error.
✅ 3) PermGen Size in Java 8
🔹 Definition:
PermGen (Permanent Generation) was a memory area in JVM used until Java 7 to store:
Class metadata
Method info
Interned strings
Static variables
🔸 In Java 8:
PermGen was removed, and replaced with Metaspace.
Metaspace stores class metadata in native memory, not the JVM heap.
It automatically resizes, avoiding the OutOfMemoryError: PermGen space issue.
🔸 Comparison:
| Feature | PermGen (Java 7) | Metaspace (Java 8+) |
|---|
| Memory type | Part of JVM heap | Native memory |
| Tuning needed | Yes | Optional |
| Scalability | Limited | Dynamic |
| Command line | -XX:PermSize, -XX:MaxPermSize | -XX:MetaspaceSize, -XX:MaxMetaspaceSize |