Search Tutorials


Top Java 21 Interview Questions (2025) | JavaInuse

Most Frequently Asked Java 21 Interview Questions


  1. What new features and improvements were introduced in Java 21?
  2. How does Java 21 handle modules and dependencies?
  3. Can you explain the concept of sealed classes in Java 21?
  4. What is the difference between records and classes in Java 21?
  5. How does the new text blocks feature in Java 21 improve code readability?
  6. What is the purpose of the JEP (JDK Enhancement Proposal) process in Java development?
  7. How does Java 21 support pattern matching for instanceof?
  8. What are some improvements made to the garbage collection process in Java 21?
  9. Can you explain the concept of foreign function interface (FFI) in Java 21?
  10. How does Java 21 address security vulnerabilities and provide enhanced security features?

What new features and improvements were introduced in Java 21?

In Java 21, several new features and improvements were introduced to enhance the language's functionality and performance. Some of the notable changes include:
1. Pattern Matching for Switch Statements: Java 21 introduced the ability to use pattern matching in switch statements, allowing for more concise and readable code. This feature makes it easier to handle different cases based on patterns rather than specific values. For example, you can now write switch statements like this:
int num = 2;
switch(num){
    case var x when x < 0 -> System.out.println("Negative number");
    case var x when x > 0 -> System.out.println("Positive number");
    case var x -> System.out.println("Zero");
}

2. Records: Java 21 introduced a new type called records, which makes it easier to create data classes with a concise syntax. Records automatically generate getters, setters, equals, hashcode, and toString methods based on the fields defined in the record. Here's an example of how you can define a record:
public record Person(String name, int age){}

3. Sealed Classes: Sealed classes were introduced in Java 21 to restrict the subclasses that can extend a particular class. This feature allows developers to define a limited set of subclasses that can extend a sealed class, providing more control over the class hierarchy. Here's an example of how you can define a sealed class:
public sealed class Shape permits Circle, Rectangle, Square{}

4. Enhanced Pattern Matching for instanceof: Java 21 introduced enhanced pattern matching for the instanceof operator, allowing developers to extract component instances from objects in a more concise way. This feature simplifies the code required to check an object's type and extract its components.
if(object instanceof Circle circle){
    double radius = circle.getRadius();
    // do something with radius
}

5. Improved Garbage Collection: Java 21 introduced improvements to the Garbage Collection process, making it more efficient and reducing the likelihood of memory leaks. These enhancements optimize memory management and overall performance of Java applications.
Overall, Java 21 brought several new features and improvements to the language, making it more powerful and developer-friendly. These enhancements are designed to simplify coding tasks, improve code readability, and enhance the overall performance of Java applications. Developers can leverage these new features to write more efficient and maintainable code.

How does Java 21 handle modules and dependencies?

Java 21 introduces a new and improved way of managing modules and dependencies through the use of the Java Platform Module System (JPMS). This system allows developers to create modular applications that are easier to maintain and scale, as well as providing better control over dependencies.
One of the key features of the JPMS is the module-info.java file, which is used to define the module and its dependencies. This file specifies the module's name, version, and dependencies on other modules. By explicitly stating dependencies, developers can ensure that their application only depends on the modules it needs, reducing the risk of conflicts and ensuring better encapsulation.
In addition to the module-info.java file, Java 21 also introduces new commands for working with modules, such as the 'java --module' flag. This flag allows developers to specify which modules to include when running their application, making it easier to manage dependencies at runtime.
To demonstrate how Java 21 handles modules and dependencies, let's consider an example where we have a simple application with two modules: a core module and a utils module. The core module depends on the utils module to perform certain operations.
First, we create a module-info.java file for each module. In the core module, the module-info.java file might look like this:
module core {
    requires utils;
}

And in the utils module, the module-info.java file might look like this:
module utils {
    exports com.example.utils;
}

In this example, the core module requires the utils module, meaning that it depends on it to function properly. The utils module exports the com.example.utils package, allowing other modules to access its functionality.
When compiling and running this application, we can use the new '--module' flag to specify the modules to include. For example, to compile and run the core module with the utils module, we could use the following commands:
javac --module-path utils -d out/core core/*.java
java --module-path out/utils -m core/com.example.Main


By using the module system introduced in Java 21, developers can create more modular and maintainable applications that are easier to scale and manage. This system provides better control over dependencies, ensuring that applications only include the modules they need to function properly.
Java 21 introduces a new and improved way of managing modules and dependencies through the use of the Java Platform Module System (JPMS). This system allows developers to create modular applications that are easier to maintain and scale, as well as providing better control over dependencies.
One of the key features of the JPMS is the module-info.java file, which is used to define the module and its dependencies. This file specifies the module's name, version, and dependencies on other modules. By explicitly stating dependencies, developers can ensure that their application only depends on the modules it needs, reducing the risk of conflicts and ensuring better encapsulation.
In addition to the module-info.java file, Java 21 also introduces new commands for working with modules, such as the 'java --module' flag. This flag allows developers to specify which modules to include when running their application, making it easier to manage dependencies at runtime.
To demonstrate how Java 21 handles modules and dependencies, let's consider an example where we have a simple application with two modules: a core module and a utils module. The core module depends on the utils module to perform certain operations.
First, we create a module-info.java file for each module. In the core module, the module-info.java file might look like this:
module core {
    requires utils;
}

And in the utils module, the module-info.java file might look like this:
module utils {
    exports com.example.utils;
}

In this example, the core module requires the utils module, meaning that it depends on it to function properly. The utils module exports the com.example.utils package, allowing other modules to access its functionality.
When compiling and running this application, we can use the new '--module' flag to specify the modules to include. For example, to compile and run the core module with the utils module, we could use the following commands:
javac --module-path utils -d out/core core/*.java
java --module-path out/utils -m core/com.example.Main

By using the module system introduced in Java 21, developers can create more modular and maintainable applications that are easier to scale and manage. This system provides better control over dependencies, ensuring that applications only include the modules they need to function properly.

Can you explain the concept of sealed classes in Java 21?

Sealed classes in Java 21 provide a way to restrict the inheritance hierarchy of a class by limiting which classes can extend them. This means that a sealed class can only be extended by a set of permitted classes that are explicitly declared. This concept adds an extra level of control and security in the Java programming language.
To create a sealed class in Java 21, the class declaration is annotated with the `sealed` keyword, followed by the list of permitted subclasses. The permitted subclasses are declared using the `permits` keyword in the same file as the sealed class. This ensures that only the permitted subclasses can extend the sealed class, and any other attempt to extend it will result in a compile-time error.
Here is an example of a sealed class in Java 21:



sealed class Shape permits Circle, Rectangle {
  
    public double calculateArea() {
        return 0;
    }
}

final class Circle extends Shape {
  
    private double radius;
  
    public Circle(double radius) {
        this.radius = radius;
    }
  
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

final class Rectangle extends Shape {
  
    private double length;
    private double width;
  
    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }
  
    @Override
    public double calculateArea() {
        return length * width;
    }
}


In this example, the `Shape` class is declared as sealed and permits only the `Circle` and `Rectangle` classes to extend it. Any other attempt to create a subclass of `Shape` will result in a compile-time error.
Sealed classes in Java 21 provide a way to ensure that the inheritance hierarchy is limited to a specific set of classes, which can be useful in scenarios where certain classes should not be extended by unauthorized classes. This concept adds an extra layer of security and control in the Java programming language, making the code more robust and maintainable.

What is the difference between records and classes in Java 21?

In Java 21, there are differences between records and classes that play a crucial role in how data is structured and manipulated.
Records are a new feature introduced in Java 16 that allow for the concise representation of immutable data. They are essentially classes that are designed for holding data and come with built-in methods like equals(), hashCode(), and toString(). Records are defined using the 'record' keyword and have a fixed set of components known as state variables, which are declared in the parentheses following the record keyword.
Classes, on the other hand, are a fundamental part of Java and have been around since its inception. They are used to define the structure and behavior of objects, allowing for encapsulation of data and functionality. Classes can have fields, methods, constructors, and can be extended or implemented by other classes or interfaces.
One key difference between records and classes is that records are implicitly final and immutable. This means that once a record object is created, its state cannot be changed. In contrast, classes are not inherently immutable, and developers need to implement their own mechanisms to ensure immutability if desired.
Another difference is that records do not explicitly define members like fields and methods; instead, they automatically generate accessors for their components. This can lead to more concise and readable code when dealing with data-only classes. On the other hand, classes require explicit definition of fields and methods, giving developers more control over the structure of their objects.
Here is an example to illustrate the difference between records and classes:

// Record definition
public record Person(String name, int age) {}

// Class definition
public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}


In the record definition, we can see that the components 'name' and 'age' are specified in the parentheses following the record keyword. Accessors for these components are automatically generated, making it more concise compared to the class definition where getters need to be explicitly defined.
In conclusion, records and classes serve different purposes in Java 21. Records are ideal for representing immutable data structures concisely, while classes offer greater flexibility and control over object structure and behavior. Understanding the differences between records and classes can help developers choose the most appropriate approach for designing their Java applications.

How does the new text blocks feature in Java 21 improve code readability?

The new text blocks feature introduced in Java 21 brings significant improvements to code readability by simplifying the way multi-line strings are handled in Java programs. Previously, developers had to concatenate multiple strings using the '+' operator or use escape characters like '\n' to create multi-line strings, which often led to messy and hard-to-read code. With text blocks, developers can now define multi-line strings directly within their code without the need for additional concatenation or escape characters.
One of the key benefits of text blocks is that they make it easier to maintain the indentation of multi-line strings, which improves code readability by preserving the structure of the text. This is particularly useful when working with large amounts of text or code snippets that need to be displayed exactly as they are. Additionally, text blocks support the inclusion of placeholders using the '$' character, which allows for dynamic content to be inserted into the string without compromising its readability.
Here is an example of how text blocks can improve code readability in Java 21:
Instead of writing:

String myString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
                + "\n sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
                + "\n Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";


With text blocks, you can write: String myString = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. """;

As shown in the example above, the use of text blocks drastically simplifies the process of defining multi-line strings, making the code much easier to read and maintain. By eliminating the need for manual string concatenation and escape characters, text blocks allow developers to focus on the content of the string rather than its formatting, resulting in cleaner and more readable code.
In conclusion, the new text blocks feature in Java 21 greatly enhances code readability by providing a more intuitive and concise way to work with multi-line strings. By streamlining the process of defining and formatting text, text blocks make it easier for developers to create and maintain clean and readable code in their Java programs.

What is the purpose of the JEP (JDK Enhancement Proposal) process in Java development?

The JEP (JDK Enhancement Proposal) process in Java development serves as a structured and transparent way for proposing, discussing, and ultimately implementing significant enhancements and new features in the Java Development Kit (JDK). This process aims to involve the Java community in decision-making and prioritizing which enhancements should be included in future releases of the JDK.
One of the main purposes of the JEP process is to gather input and feedback from developers, users, and other stakeholders to ensure that new features and enhancements meet the needs of the Java community. By clearly defining the proposal, objectives, and design of a potential enhancement, the JEP process allows for thorough evaluation and discussion of each proposal before it is implemented.
Furthermore, the JEP process helps to prioritize and schedule enhancements by providing a structured framework for evaluating the impact, feasibility, and compatibility of proposed changes. This helps to ensure that resources are allocated effectively and that the most important enhancements are included in upcoming JDK releases.
Additionally, the JEP process promotes transparency and accountability in the development of the JDK by providing a public record of proposed enhancements, discussions, decisions, and actions taken. This allows the Java community to stay informed about the progress of proposed enhancements and to provide feedback throughout the development process.
An example of a recent JEP proposal is JEP 376: ZGC: Concurrent Thread-Stack Processing. This proposal aims to improve the performance of the Z Garbage Collector (ZGC) by processing thread stacks concurrently with the Java threads. By implementing this enhancement, it is expected to reduce the pause times of the ZGC and improve the overall performance of Java applications.
Below is a simplified code snippet illustrating how the proposed enhancement in JEP 376 could be implemented:

public class ThreadStackProcessor {
    public void processThreadStack(Thread thread) {
        // Process the stack of the given thread concurrently
    }
}

public class Application {
    public static void main(String[] args) {
        Thread thread = new Thread();
        ThreadStackProcessor processor = new ThreadStackProcessor();
        
        processor.processThreadStack(thread);
        
        // Continue with the rest of the application
    }
}


In conclusion, the JEP process in Java development serves as a valuable mechanism for proposing, evaluating, and implementing enhancements to the JDK. By promoting community involvement, prioritization, transparency, and accountability, the JEP process helps to ensure that the Java platform continues to evolve and meet the needs of its users.

How does Java 21 support pattern matching for instanceof?

In Java 21, there is a new feature introduced known as enhanced pattern matching for instanceof. This feature allows developers to write more concise and readable code when working with instanceof checks.
With traditional instanceof checks, developers would typically use a combination of instanceof and casting to determine the type of an object. This often resulted in verbose and error-prone code. However, with enhanced pattern matching for instanceof, this process has been simplified and made more intuitive.
One of the key improvements in Java 21 is the introduction of the pattern matching instanceof operator. This new operator allows developers to combine an instanceof check with a pattern variable declaration in a single line of code. This not only makes the code more concise but also improves its readability.
Here is an example of how the pattern matching instanceof operator can be used in Java 21:

public class Main {
    public static void main(String[] args) {
        
        Object obj = "Hello world";
        
        if (obj instanceof String str) {
            System.out.println("The object is a String: " + str);
        } else {
            System.out.println("The object is not a String");
        }
    }
}


In the code snippet above, we have an object obj that we are checking to see if it is an instance of a String. If it is, we can directly assign it to a new variable str within the if statement block and use it without any casting. This makes the code more concise and eliminates the need for explicit casting.
In addition to the pattern matching instanceof operator, Java 21 also introduces the concept of exhaustiveness checking. This means that if all possible cases are not covered in a pattern matching instanceof check, the compiler will generate a warning. This helps to catch potential bugs or oversights in the code at compile-time.
Overall, the enhanced pattern matching for instanceof in Java 21 provides a more elegant and concise way to work with instanceof checks, making code easier to read and maintain. By leveraging this new feature, developers can write cleaner and more efficient code without sacrificing readability or safety.

What are some improvements made to the garbage collection process in Java 21?

In Java 21, there have been several significant enhancements made to the garbage collection process aimed at improving memory management efficiency and overall performance of Java applications. One key improvement is the introduction of a new garbage collection algorithm called ZGC, which stands for Z Garbage Collector. This algorithm is designed to reduce garbage collection pauses to the bare minimum, thus improving the responsiveness and scalability of Java applications.
Another improvement in Java 21 is the optimization of the existing garbage collection algorithms like G1 and Parallel GC. These algorithms have been fine-tuned to better handle large heaps and workloads, resulting in faster and more efficient garbage collection cycles. The G1 garbage collector now features improved CPU utilization and reduced pause times, making it a more attractive option for applications with large heaps.
Additionally, Java 21 introduces the concept of metaspace resizing, which allows the JVM to dynamically adjust the size of the metaspace based on the application's memory requirements. This feature prevents metaspace from growing too large and consuming excessive memory, leading to improved overall memory utilization and reduced risk of OutOfMemoryErrors.
One more improvement in Java 21 is the enhancement of garbage collection logging and monitoring capabilities. The JVM now provides more detailed and insightful information about garbage collection events, making it easier for developers to analyze and optimize their applications' memory usage. By utilizing the new garbage collection logging features, developers can identify memory bottlenecks and fine-tune their application settings for optimal performance.
Here is a sample code snippet demonstrating the implementation of metaspace resizing in Java 21:

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryType;

public class MetaspaceResizingExample {
    public static void main(String[] args) {
        MemoryPoolMXBean metaspacePool = ManagementFactory.getMemoryPoolMXBeans().stream()
                .filter(pool -> pool.getType() == MemoryType.NON_HEAP && pool.getName().contains("Metaspace"))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("Metaspace memory pool not found"));

        long currentMetaspaceUsage = metaspacePool.getUsage().getUsed();
        long maxMetaspaceSize = metaspacePool.getUsage().getMax();

        if (currentMetaspaceUsage > maxMetaspaceSize * 0.9) {
            long newMetaspaceSize = (long) (maxMetaspaceSize * 1.5);
            metaspacePool.setUsageThreshold(newMetaspaceSize);
            metaspacePool.setUsageThresholdExceeded(true);
            System.out.println("Metaspace size increased to: " + newMetaspaceSize);
        }
    }
}


In conclusion, the improvements made to the garbage collection process in Java 21 have significantly enhanced the memory management capabilities of the JVM, resulting in faster, more efficient, and more reliable Java applications. These enhancements address common issues like long garbage collection pauses, excessive memory consumption, and poor monitoring capabilities, making Java 21 a more attractive platform for developing high-performance applications.

Can you explain the concept of foreign function interface (FFI) in Java 21?

The foreign function interface (FFI) in Java 21 is a mechanism that allows Java code to interact with code written in other programming languages, such as C, C++, or Rust. This is achieved by providing a way for Java code to call functions in external libraries and vice versa.
In simpler terms, FFI enables Java programs to utilize functionalities from external libraries that are not written in Java. This is especially useful when working with legacy code or when trying to leverage the performance benefits of languages like C or C++, while still enjoying the flexibility and convenience of Java.
One of the key benefits of FFI is its ability to bridge the gap between different programming languages, allowing developers to take advantage of the strengths of each language without having to completely rewrite existing code. This can lead to faster development times and improved overall performance of the software.
To demonstrate the concept of FFI in Java, let's consider a simple example where we have a Java program that needs to call a C function to perform a mathematical calculation. We can achieve this using JNA (Java Native Access), a popular library for FFI in Java.
First, we need to define the interface to the C function in Java. Here's an example of how this can be done:

import com.sun.jna.Library;
import com.sun.jna.Native;

public interface MathLibrary extends Library {
    MathLibrary INSTANCE = (MathLibrary) Native.loadLibrary("mathLibrary", MathLibrary.class);
    
    int add(int a, int b);
}



In this code snippet, we define an interface `MathLibrary` that extends `Library` from JNA. We then declare a method `add` that corresponds to the C function we want to call, which takes two `int` parameters and returns an `int`.
Next, we need to implement the C function in a shared library (e.g., a dynamic link library or a shared object) that the Java program can load at runtime. Here's an example of how the C function can be implemented in a file named `mathLibrary.c`:
c
#include <stdio.h>

int add(int a, int b) {
    return a + b;
}


We then compile the C code into a shared library named `mathLibrary` (e.g., `mathLibrary.dll` on Windows or `libmathLibrary.so` on Linux) using the appropriate compiler for the target platform.
Finally, we can use the Java interface we defined earlier to call the C function from our Java program:
public class Main {
    public static void main(String[] args) {
        MathLibrary mathLibrary = MathLibrary.INSTANCE;
        
        int result = mathLibrary.add(10, 20);
        System.out.println("The result is: " + result);
    }
}


When we run the Java program, it will load the shared library containing the C function, call the `add` method defined in the interface, and print the result to the console.
In conclusion, the foreign function interface (FFI) in Java 21 provides a powerful mechanism for integrating Java code with external libraries written in other programming languages. By using FFI, developers can harness the strengths of multiple languages and create more robust and efficient software solutions.

How does Java 21 address security vulnerabilities and provide enhanced security features?

Java 21 has implemented several security enhancements and measures to address security vulnerabilities and provide enhanced security features. One of the key improvements is the implementation of the JEP 411: Deprecating Datagram Transport Layer Security (DTLS) for the Java platform. This deprecation helps to eliminate potential security risks associated with DTLS and encourages developers to use more secure alternatives.
Another significant enhancement in Java 21 is the introduction of the JEP 411: Enhanced Secure Socket Extension (SSE). This feature provides an enhanced version of the Secure Socket Extension (SSE) API, which offers improved security capabilities for network communication. The enhanced SSE API includes support for modern cryptographic algorithms, stronger encryption protocols, and better key management practices. This helps to safeguard sensitive data and protect communication channels from potential security threats.
Furthermore, Java 21 includes the JEP 411: Secure Classloading in an Enhanced Platform Module System. This enhancement aims to enhance classloading mechanisms in the Java platform to prevent malicious code execution and improve overall system security. The secure classloading feature includes better isolation and encapsulation of modules, stricter access controls, and improved classpath handling to minimize security vulnerabilities and strengthen the platform's security posture.
In addition to these enhancements, Java 21 also implements the JEP 411: Secure Random Generation and Improved KeyStore Management. This feature focuses on improving random number generation processes and key management practices to enhance the security of cryptographic operations in Java applications. The enhanced secure random generation and key management features help to mitigate vulnerabilities related to predictable or weak cryptographic keys, ensuring the integrity and confidentiality of sensitive data.
Moreover, Java 21 introduces the JEP 411: Enhanced Security Manager for Fine-Grained Security Policies. This enhancement allows developers to implement fine-grained security policies for Java applications to define and enforce specific security rules and restrictions. The enhanced security manager feature enables better control over access permissions, resource usage, and code execution privileges, helping to prevent unauthorized actions and mitigate potential security risks.
To demonstrate the implementation of enhanced security features in Java 21, consider the following sample code snippet that showcases the usage of the enhanced Secure Socket Extension (SSE) API for secure network communication:
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.Socket;

public class SecureSocketDemo {

    public static void main(String[] args) {
        try {
            SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            Socket socket = sslSocketFactory.createSocket("secure.server.com", 443);

            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            out.println("GET /index.html HTTP/1.1");
            out.println("Host: secure.server.com");
            out.println();

            String response;
            while ((response = in.readLine()) != null) {
                System.out.println(response);
            }

            in.close();
            out.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

In this code snippet, we demonstrate how to establish a secure connection using the enhanced Secure Socket Extension (SSE) API in Java 21. By creating a secure socket with an SSL socket factory and sending secure HTTP requests to a server, developers can leverage the enhanced security features in Java 21 to protect network communication channels and safeguard sensitive data from potential security vulnerabilities.