Top Java 21 Interview Questions and Answers (Comprehensive 2026 Guide)

Top Java 21 Interview Questions and Answers (Comprehensive Guide)

Java 21 is a Long-Term Support (LTS) release that fundamentally transformed how Java developers handle concurrency, data extraction, and collection management. Features like Virtual Threads and Pattern Matching have rapidly become standard expectations in technical interviews for backend engineering roles.

To help you pass your next technical interview, we have compiled the most critical Java 21 questions. Rather than providing thin, surface-level definitions, this guide delivers deep, architectural explanations and production edge cases that prove to interviewers you truly understand the framework mechanics.


1. Virtual Threads (Project Loom)

Q1: What are Virtual Threads in Java 21, and how do they differ from Platform Threads under the hood?

Historically, a Java Thread was directly mapped 1:1 to an Operating System (OS) platform thread. Because OS threads reserve around 1MB of stack memory and require expensive kernel-level context switches, applications could only scale to a few thousand concurrent threads before encountering OutOfMemoryError.

Virtual Threads break this constraint through an M:N mapping model managed entirely by the Java Virtual Machine (JVM). The JVM utilizes a small, fixed pool of OS threads called Carrier Threads. When a Virtual Thread hits a blocking I/O operation, the JVM copies its call stack to the heap and unmounts it, immediately freeing the Carrier Thread to execute another Virtual Thread. This allows developers to write simple, synchronous, blocking code that scales to millions of concurrent requests.

Q2: Should you use Thread Pools for Virtual Threads?

No, pooling Virtual Threads is considered a severe anti-pattern. Thread pools (like ThreadPoolExecutor) were invented specifically to recycle expensive OS platform threads because the computational cost of creating and destroying them was too high.

Virtual Threads are incredibly cheap—similar to creating a standard Java object like a String. In Java 21, you must adopt a task-per-thread model where you spawn a brand new virtual thread for every incoming request or asynchronous task, and allow the Garbage Collector to clean it up when the task completes.

// Correct Java 21 Approach: No pooling, just task-per-thread
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> fetchFromDatabase());
    executor.submit(() -> callExternalApi());
}
Q3: What does "Thread Pinning" mean, and how do you fix it in production?

Thread Pinning is a critical performance bottleneck where a Virtual Thread becomes "stuck" to its underlying OS Carrier Thread and cannot be unmounted during a blocking operation. When this happens, the Carrier Thread is blocked, starving the JVM's ability to schedule other virtual threads and severely degrading application throughput.

In Java 21, pinning is primarily caused by executing blocking I/O operations inside a legacy synchronized block or method. To resolve this, developers must trace pinned threads using Java Flight Recorder (JFR) and replace synchronized blocks with ReentrantLock, which parks threads at the JVM level and fully supports Virtual Thread unmounting.

Q4: Are Virtual Threads suitable for CPU-bound tasks?

No. Virtual Threads excel exclusively in I/O-bound workloads (e.g., waiting for database queries, network responses, or file system reads) because they can unmount while waiting, freeing up the CPU for other tasks.

For CPU-bound tasks (e.g., video rendering, cryptographic hashing), the thread never waits; it continuously uses the processor. If you use Virtual Threads for heavy computation, they will monopolize the underlying Carrier Threads, leading to Carrier Starvation and zero performance gains over standard parallel streams or a ForkJoinPool.

Q5: How do Virtual Threads impact database connection pooling (like HikariCP)?

Because Virtual Threads are so lightweight, a Spring Boot application might effortlessly spawn 50,000 concurrent virtual threads under heavy load. However, your relational database (like PostgreSQL) and your HikariCP pool cannot handle 50,000 concurrent connections.

If left unconfigured, thousands of Virtual Threads will flood the connection pool queues simultaneously, resulting in severe lock contention and widespread ConnectionTimeoutException errors. When migrating to Virtual Threads, you must decouple your thread limits from your database limits by implementing strict bulkheads or Semaphores at the application layer to throttle how many virtual threads can access the pool at once.

Q6: What Garbage Collection (GC) considerations are necessary for Virtual Threads?

Unlike platform threads that allocate stack memory at the OS level, Virtual Threads allocate their stack frames directly on the JVM Heap when they unmount. If you have millions of active Virtual Threads constantly mounting and unmounting, this generates massive short-lived object pressure in the young generation space.

To prevent frequent "Stop-the-World" pauses under massive Loom concurrency, production systems should consider switching to the Generational ZGC (Z Garbage Collector) or tuning G1GC, as these modern collectors are highly optimized for sweeping millions of short-lived heap allocations concurrently with minimal pause times.


2. Pattern Matching and Records

Q7: How does Pattern Matching for switch improve code quality?

Before Java 21, switch statements were limited to primitives, Strings, and Enums. If you needed to execute conditional logic based on an object's class type, you were forced to write a long, messy chain of if-else statements using instanceof and explicit casting.

Pattern Matching for switch allows you to pass an object directly into a switch statement, verify its type, and cast it to a variable in a single, fluid expression.

public String formatData(Object data) {
    return switch (data) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case String s  -> String.format("String %s", s);
        default        -> data.toString();
    };
}
Q8: What are Record Patterns, and how do they handle deep deconstruction?

Record Patterns allow you to deconstruct a Record into its component fields directly inside an instanceof check or a switch case. Instead of verifying the type and manually calling getter methods, the JVM automatically unwraps the fields and binds them to local variables.

Furthermore, Record Patterns can be deeply nested. If a Payload record contains a Metadata record, which contains a Location record, you can deconstruct all three levels simultaneously in a single line of code, vastly simplifying JSON parsing logic.

public record User(String name, int age) {}

public void printUserInfo(Object obj) {
    // Java 21 Record Pattern Deconstruction
    if (obj instanceof User(String name, int age)) {
        System.out.println(name + " is " + age + " years old.");
    }
}
Q9: Explain the purpose of Unnamed Variables (the _ character).

When deconstructing large records using Record Patterns, you often do not need every single field. Previously, you had to declare variables for fields you didn't intend to use, which cluttered the code and triggered "unused variable" warnings in IDEs.

Java 21 introduced the underscore _ as the unnamed variable. It acts as a placeholder that tells the compiler you acknowledge the field exists in the pattern, but you intentionally want to ignore its value.

// We only care about the email; the name field is ignored
if (obj instanceof User(_, String email)) {
    sendEmail(email);
}
Q10: How do Sealed Classes mathematically guarantee "Exhaustiveness" in Pattern Matching?

If a switch statement is used as an expression (meaning it returns a value), the Java compiler enforces exhaustiveness—you must cover every possible type. Historically, this meant writing a mandatory, dead-code default block.

Sealed Classes explicitly enumerate their exact implementations via the permits clause. Because the compiler knows exactly how many subclasses exist, it can verify exhaustiveness at compile-time. If you write a switch case for every permitted subclass, you can safely omit the default case. If another developer adds a new subclass later, the compiler will instantly break the build, transforming potential runtime failures into compile-time safety.


3. Sequenced Collections

Q11: What problem does the Sequenced Collections API solve?

For decades, the Java Collections Framework lacked a unified interface for collections that had a defined, predictable encounter order. Accessing the first or last element of a List, a Deque, or a LinkedHashSet required completely different, inconsistent methods (like list.get(size - 1) vs deque.getLast()).

Java 21 introduced the SequencedCollection interface to unify this behavior across all ordered collections. It provides standardized methods: getFirst(), getLast(), addFirst(), addLast(), removeFirst(), and removeLast().

Q12: Does the reversed() method in Sequenced Collections duplicate the data?

No. The reversed() method does not iterate through the collection and copy elements into a new list. Instead, it returns a highly efficient, lightweight reversed view of the original collection.

Because it is merely adjusting traversal pointers, the operation executes in O(1) time complexity. However, interviewers will look for you to note the side effect: any structural modifications (like adding or removing elements) made to the original collection will immediately reflect in the reversed view.


4. Scoped Values and Structured Concurrency (Previews)

Q13: Why are Scoped Values introduced as a replacement for ThreadLocal?

ThreadLocal has major architectural flaws: it is mutable, it requires manual cleanup via remove(), and its lifespan is unbounded. With Virtual Threads, an application might spawn millions of threads. Creating millions of mutable ThreadLocal maps will quickly consume gigabytes of heap storage and cause catastrophic memory leaks if developers forget to clean them up.

Scoped Values provide an immutable context that is strictly bounded to a specific lexical scope (a specific block of code). Once the block of code finishes executing, the Scoped Value is automatically and safely discarded by the JVM, guaranteeing absolute memory safety.

Q14: How does Structured Concurrency prevent "Thread Leaks"?

In standard asynchronous programming using CompletableFuture, if a parent method triggers three concurrent sub-tasks and the first one fails, the other two continue running blindly in the background, consuming CPU and network resources (a thread leak).

Structured Concurrency uses StructuredTaskScope to group related sub-tasks into a single syntactic block, establishing a parent-child relationship. If one child task fails, the scope automatically intercepts the failure and sends immediate interruption signals to all sibling tasks, cancelling them instantly. This prevents orphan threads from wasting resources.


5. String Templates (Preview)

Q15: How do String Templates improve upon standard String concatenation?

Prior to Java 21, combining strings and variables required messy + concatenation or verbose String.format() calls, both of which are difficult to read and highly prone to injection vulnerabilities (like SQL injection).

String Templates (a preview feature) allow developers to safely embed expressions directly inside string literals using the \{} syntax. More importantly, template processors (like STR or FMT) can intercept, validate, and sanitize the interpolated values before the final string is constructed, making it significantly safer for generating JSON, HTML, or SQL queries.

// Java 21 String Template (Preview)
String name = "Alice";
int age = 28;
String json = STR."{ \"user\": \"\{name}\", \"age\": \{age} }";

๐Ÿ“š Further Java Interview Preparation

Continue mastering the Java ecosystem by exploring our most popular architectural and technical interview guides below.

๐Ÿ•— Java 8 Interview Questions

Revisit Streams, Lambdas, and functional programming foundations that dictate modern Java data pipelines.

๐Ÿ“ฆ Java Collections Interview Questions

Understand internal structural complexities, map bucket mechanics, and core algorithmic choices.

⚙️ Java Multithreading Interview Questions

Master hardware threads, execution memory barriers, and core platform threading primitives.

✍️ Java Coding Round Questions

Practice writing clean, high-performance Java code that implements patterns effectively under pressure.