Master Core Java Programming From Scratch

Clear, interactive, and structured coding lessons designed for absolute beginners.

Module 9

Java Features

Explore important modern Java features introduced from Java 8 through recent Java releases.

🚀

1. Introduction to Java

Modern Java has evolved significantly beyond traditional object-oriented programming. New language features and APIs have improved readability, safety, performance, and developer productivity.

Some of the most important modern Java features include:

  • Lambda Expressions
  • Functional Interfaces
  • Stream API
  • Optional
  • Local variable type inference using var
  • Switch expressions
  • Pattern matching
  • Text blocks
  • Records
  • Sealed classes
  • Private interface methods
  • Modern Date and Time API

2. Java 8 Features

Java 8 was one of the most important releases in Java history. It introduced functional programming capabilities and major API improvements.

Feature Purpose
Lambda Expressions Write concise function-like behavior.
Functional Interfaces Represent a single abstract operation.
Stream API Process collections declaratively.
Optional Represent potentially absent values.
Default Methods Add behavior to interfaces.
java.time Modern date and time handling.

3. Lambda Expressions

Lambda expressions provide a concise way to represent behavior.

Java
List<String> names =
    List.of("Amit", "Neha", "Rahul");

names.forEach(
    name -> System.out.println(name)
);

Lambda expressions are commonly used with functional interfaces, collections, and streams.

4. Functional Interfaces

A functional interface has exactly one abstract method. Examples include Predicate, Function, Consumer, and Supplier.

Java
Predicate<Integer> positive =
    number -> number > 0;

System.out.println(
    positive.test(10)
);

5. Stream API

The Stream API allows collections to be processed using a declarative pipeline of operations.

Java
List<Integer> numbers =
    List.of(1, 2, 3, 4, 5, 6);

List<Integer> evenNumbers =
    numbers.stream()
           .filter(n -> n % 2 == 0)
           .toList();

System.out.println(evenNumbers);

Common stream operations include:

  • filter()
  • map()
  • sorted()
  • distinct()
  • limit()
  • reduce()
  • collect()
  • toList()

6. Optional

Optional represents a value that may or may not be present. It can help make absence explicit and reduce some common null-handling problems.

Java
Optional<String> name =
    Optional.ofNullable(null);

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

System.out.println(result);
Best practice: Optional is primarily intended as a return type for methods where absence is a meaningful result. It should not automatically replace every nullable field or parameter.

7. var - Local Variable Type Inference

Java 10 introduced var for local variable type inference. The compiler determines the variable's static type from its initializer.

Java
var name = "Samadhan";
var age = 22;
var salary = 85000.50;

System.out.println(name);
System.out.println(age);
System.out.println(salary);

The variable still has a fixed compile-time type. Java is not becoming dynamically typed.

8. Switch Expressions

Modern switch expressions can directly produce a value.

Java
int day = 2;

String result = switch (day) {

    case 1 -> "Monday";
    case 2 -> "Tuesday";
    case 3 -> "Wednesday";

    default -> "Other";
};

System.out.println(result);

Switch expressions make many branching operations more concise.

9. Pattern Matching

Pattern matching simplifies type checks and conditional logic.

Java
Object value = "Java";

if (value instanceof String text
        && text.length() > 3) {

    System.out.println(
        text.toUpperCase()
    );
}

The variable text is available after the pattern has successfully matched and the condition permits its use.

10. Text Blocks

Text blocks provide a convenient syntax for multiline string literals.

Java
String json = """
        {
            "name": "Java",
            "version": 21
        }
        """;

System.out.println(json);

Text blocks are useful for JSON, HTML, SQL, XML, and other multiline text.

11. Records

Records provide a concise way to model data-centric classes.

Java
record Employee(
    int id,
    String name,
    double salary
) {
}

Employee employee =
    new Employee(101, "Sejl", 50000);

System.out.println(
    employee.name()
);

Records automatically provide important members such as accessors, equals(), hashCode(), and toString().

12. Sealed Classes

Sealed classes and interfaces allow developers to explicitly restrict which types can directly extend or implement a type.

Java
sealed interface Payment
    permits CardPayment, CashPayment {
}

final class CardPayment
    implements Payment {
}

final class CashPayment
    implements Payment {
}

This makes the inheritance hierarchy explicit and controlled.

13. Private Interface Methods

Java 9 introduced private methods in interfaces. They allow common implementation logic to be shared by default and static methods.

Java
interface Logger {

    default void info(String message) {

        System.out.println(
            format(message)
        );
    }

    private String format(String message) {

        return "[INFO] " + message;
    }
}

14. Modern Date and Time API

Java 8 introduced the java.time API, which provides immutable and thread-safe date and time types.

Java
LocalDate today =
    LocalDate.now();

LocalDate nextWeek =
    today.plusWeeks(1);

System.out.println(today);
System.out.println(nextWeek);

Common classes include:

  • LocalDate
  • LocalTime
  • LocalDateTime
  • ZonedDateTime
  • Instant
  • Duration
  • Period

15. Convenient Collection Factory Methods

Modern Java provides convenient factory methods for creating unmodifiable collections.

Java
List<String> names =
    List.of("Amit", "Neha", "Rahul");

Set<Integer> numbers =
    Set.of(10, 20, 30);

Map<Integer, String> employees =
    Map.of(
        101, "Amit",
        102, "Neha"
    );
These factory methods return unmodifiable collections. Attempts to structurally modify them will result in an exception.

16. Modern HTTP Client

Java 11 introduced the standard HttpClient API for making HTTP requests.

Java
HttpClient client =
    HttpClient.newHttpClient();

HttpRequest request =
    HttpRequest.newBuilder()
        .uri(
            URI.create(
                "https://example.com"
            )
        )
        .GET()
        .build();

HttpResponse<String> response =
    client.send(
        request,
        HttpResponse.BodyHandlers.ofString()
    );

System.out.println(
    response.statusCode()
);

17. Modern File API

The NIO.2 API provides modern file and path handling through classes such as Path and Files.

Java
Path path =
    Path.of("data.txt");

Files.writeString(
    path,
    "Hello Java"
);

String content =
    Files.readString(path);

System.out.println(content);

18. Modern Stream Collection

Recent Java versions provide convenient terminal operations such as toList().

Java
List<String> result =
    names.stream()
         .filter(
             name -> name.length() > 4
         )
         .toList();

System.out.println(result);

19. Modern String Methods

Modern Java versions have added useful methods to the String API.

Java
String text = "Java\nPython\nC#";

text.lines()
    .forEach(
        line -> System.out.println(line)
    );

Other useful APIs include methods such as isBlank(), strip(), stripLeading(), stripTrailing(), and repeat().

20. Pattern Matching with Switch

Modern Java versions have expanded switch to support pattern-based logic.

Java
static String describe(Object value) {

    return switch (value) {

        case Integer number
            -> "Integer: " + number;

        case String text
            -> "String: " + text;

        case null
            -> "Null value";

        default
            -> "Other type";
    };
}

Pattern matching can make complex type-based branching easier to read.

21. Combining Modern Features

Modern Java features can be combined to create concise and strongly typed domain models.

Java
sealed interface Result
    permits Success, Failure {
}

record Success(String message)
    implements Result {
}

record Failure(String error)
    implements Result {
}

static String process(Result result) {

    return switch (result) {

        case Success success
            -> "Success: "
                + success.message();

        case Failure failure
            -> "Failure: "
                + failure.error();
    };
}

22. Important Java Feature Timeline

Java Version Important Features
Java 8 Lambda, Stream API, Optional, default methods, java.time
Java 9 Private interface methods, module system
Java 10 Local variable type inference with var
Java 11 Standard HttpClient and additional APIs
Java 14 Switch expressions standardized
Java 15 Text blocks standardized
Java 16 Records and pattern matching for instanceof standardized
Java 17 Sealed classes standardized
Java 21 Pattern matching for switch and record patterns standardized

23. Benefits of Modern Java

  • Cleaner and more readable code.
  • Reduced boilerplate.
  • Better support for functional programming.
  • Improved collection processing.
  • Safer and clearer null handling.
  • Better domain modeling.
  • More expressive control flow.
  • Improved API design.
  • Modern date and time handling.
  • Better support for concurrent and asynchronous programming.

24. Best Practices

  • Use modern features when they improve readability.
  • Do not use var when the inferred type is unclear.
  • Use streams when collection processing becomes clearer.
  • Avoid unnecessarily complex stream pipelines.
  • Use records for data-centric immutable models.
  • Use sealed types when a hierarchy should be intentionally restricted.
  • Prefer java.time over legacy date/time classes.
  • Use Optional thoughtfully rather than everywhere.
  • Keep modern code understandable for the team maintaining it.

25. Interview Questions

Lambda expressions provide a concise way to represent behavior and are commonly used with functional interfaces.

The Stream API provides a declarative way to process sequences of data using operations such as filter, map, sorted, and reduce.

var enables local variable type inference. The compiler still assigns the variable a specific static type.

A record is a concise Java type designed primarily for modeling data.

A sealed class restricts which classes are allowed to directly extend it.

Local variable type inference using var was introduced in Java 10.

Sealed classes became a standard Java feature in Java 17.

Final Module Summary

You have now completed the major Modern Java topics:

  • var and local variable type inference
  • Switch expressions
  • Pattern matching
  • Text blocks
  • Records
  • Sealed classes
  • Modern interfaces
  • Modern Java APIs and language features

These features help Java developers write cleaner, safer, more expressive, and maintainable applications.