Thursday, 4 July 2024

Java 8 features with explanation

 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

FeatureLambda Expression Example
No arguments() -> System.out.println("Hi")
One argumentx -> 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

✅ Example: Custom Functional Interface

@FunctionalInterface interface MyPrinter { void print(String message); // Single abstract method }

✅ Usage with Lambda:

MyPrinter printer = msg -> System.out.println("Message: " + msg); printer.print("Hello Functional Interface!");

✅ Common Built-in Functional Interfaces (in java.util.function)

InterfaceMethodUse CaseLambda Example
Predicate<T>test(T)Returns booleanx -> x > 10
Function<T, R>apply(T)Converts type T to type Rs -> s.length()
Consumer<T>accept(T)Takes a value and returns nothingx -> System.out.println(x)
Supplier<T>get()Provides a value() -> "Hello"
UnaryOperator<T>apply(T)Operates on a single type Tx -> x * x
BinaryOperator<T>apply(T, T)Operates on two values of same type(a, b) -> a + b

✅ Examples:

🔹 Predicate<T>:

Predicate<String> isLong = s -> s.length() > 5; System.out.println(isLong.test("hello")); // false

🔹 Function<T, R>:

Function<String, Integer> lengthFunction = s -> s.length(); System.out.println(lengthFunction.apply("Java")); // 4

🔹 Consumer<T>:

Consumer<String> printer = s -> System.out.println(s); printer.accept("Lambda Rocks!");

🔹 Supplier<T>:

Supplier<Double> randomSupplier = () -> Math.random(); System.out.println(randomSupplier.get());

✅ 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 provides a powerful way to process sequences of elements (such as collections) 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:


String value = getValue(); // might return null if (value != null) { System.out.println(value.length()); }

With Optional:

Optional<String> value = getValue(); value.ifPresent(v -> System.out.println(v.length()));

✅ Creating Optional Instances:

Optional<String> opt1 = Optional.of("Hello"); // Non-null value Optional<String> opt2 = Optional.ofNullable(null); // Might be null Optional<String> opt3 = Optional.empty(); // No value
Method Description
Optional.of(value) Throws NullPointerException if value is null Optional.ofNullable(value) Allows null Optional.empty() Returns an empty Optional

✅ Common Methods of Optional:

MethodDescription
isPresent()Returns true if value is present
ifPresent(Consumer)Executes block if value is present
get()Returns the value (throws exception if absent)
orElse(default)Returns value or default
orElseGet(Supplier)Returns value or lazy-default
orElseThrow()Throws NoSuchElementException if empty
map(Function)Transforms value if present
flatMap(Function)Like map, but flattens nested Optional
filter(Predicate)Returns Optional if value passes condition

Example 1:

Optional<String> optional = Optional.of("Hello");

optional.ifPresent(System.out::println);

Example 2:

public Optional<String> getUserNameById(int id) {

    if (id == 1) {

        return Optional.of("Alice");

    } else {

        return Optional.empty();

    }

}

// Usage

Optional<String> name = getUserNameById(1);

// Safe access

name.ifPresent(n -> System.out.println("User: " + n));

// Default value

String result = name.orElse("Unknown");

System.out.println(result);

✅ Real-world Use Case:

public Optional<User> findByEmail(String email) {
return userRepository.stream() .filter(u -> u.getEmail().equals(email)) .findFirst(); // Returns Optional<User> } findByEmail("john@email.com") .map(User::getName) .ifPresent(System.out::println);

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);