Modern Java interview questions 40 questions in 6 chapters

  1. 01Java 8 core features7 questions
  2. 02Java 9 to 115 questions
  3. 03Java 12 to 177 questions
  4. 04Java 18 to 218 questions
  5. 05Advanced Java and JVM8 questions
  6. 06Production scenarios5 questions

Java 8 core features

Lambdas, streams, CompletableFuture, Optional and interface default methods.

  • 1.1 What are functional interfaces and lambdas? Easy 2 min

    A functional interface is an interface with exactly one abstract method, like Runnable, Function or Predicate. A lambda is just a short way to write an implementation of that one method, so I can pass behavior around like data. The FunctionalInterface annotation is optional; it only asks the compiler to fail if someone adds a second abstract method.

    Read the full answer
  • 1.2 What is the difference between map() and flatMap()? Easy 1 min

    Map turns each element into exactly one new element, so a stream of users becomes a stream of names. FlatMap turns each element into a whole stream and then joins all those small streams into one. I use it when each item has many children, like orders with line items, and I want one flat stream instead of a stream of lists.

    Read the full answer
  • 1.3 How does the Stream API work internally? Medium 1 min

    A stream is a pipeline: a source, some intermediate steps like filter and map, and one terminal step like collect. The intermediate steps only describe the work. When the terminal step runs, each element flows through all the steps one at a time, not stage by stage. A stream can be used once, and parallel streams split the source and run on the common fork-join pool.

    Read the full answer
  • 1.4 What is lazy evaluation in streams? Medium 1 min

    Intermediate operations like filter and map do nothing on their own. Work only starts when a terminal operation runs, and then the stream does only as much as it needs. That is why short-circuit operations like findFirst, anyMatch or limit can stop early, even on an infinite stream. It also means a pipeline with no terminal operation never executes, so side effects inside it never happen.

    Read the full answer
  • 1.5 What is the difference between Future and CompletableFuture? Medium 1 min

    A Future only lets you check if the work is done, cancel it, or block on get to wait for the result. CompletableFuture, added in Java 8, lets me chain the next steps without blocking: transform the result, call another async step, combine two results, or wait for many. It also handles errors in the chain. For blocking I/O, I pass my own executor instead of using the common pool.

    Read the full answer
  • 1.6 How does Optional help avoid null checks? Easy 1 min

    Optional puts "there might be no value" into the method's return type, so the caller can't forget to handle it. Instead of if-not-null checks, I chain map, filter and then orElse, orElseGet or orElseThrow. The Javadoc says it is mainly meant for return values, so I don't use it for fields or parameters, and an Optional variable itself should never be null.

    Read the full answer
  • 1.7 What is the difference between default and static methods in interfaces? Easy 1 min

    A default method has a body inside the interface and is inherited, so implementing classes get it for free but can override it. It was added so the JDK could add methods like forEach to existing interfaces without breaking every implementation. A static interface method belongs only to the interface; you call it through the interface name, and implementing classes don't inherit it.

    Read the full answer

Java 9 to 11

Modules, collection factories, private interface methods, var and new String methods.

  • 2.1 What is JPMS, the Java module system? Medium 1 min

    Java 9 added modules. A module descriptor says what the module is called, which modules it needs, and which packages it exports. That gives two things: missing dependencies fail at startup instead of at runtime, and packages that are not exported are truly hidden, even from reflection by default. The JDK itself was split into modules, which also lets you build a small custom runtime with jlink.

    Read the full answer
  • 2.2 What is the difference between List.of() and Arrays.asList()? Easy 1 min

    List.of, from Java 9, gives a truly unmodifiable list: add, set and remove all throw, and it rejects null elements. Arrays.asList gives a fixed-size view over an array: I can't add or remove, but I can set an element, and that change writes through to the original array. Arrays.asList also allows nulls. So List.of is safer for constants and return values.

    Read the full answer
  • 2.3 Why were private interface methods added? Easy 1 min

    Once interfaces had default methods, several of them often needed the same helper code. Before Java 9, the only option was another default or static method, which made that helper part of the public API. Since Java 9, an interface can have private methods, both instance and static, so default methods can share logic without exposing it to implementing classes or callers.

    Read the full answer
  • 2.4 What is var and where can you use it? Easy 1 min

    Var, added in Java 10, lets the compiler infer a local variable's type from what's on the right side. It is still fully static typing; the type is fixed at compile time. It only works for local variables with an initializer, loop variables, and, since Java 11, lambda parameters. It can't be used for fields, method parameters or return types, and var x = null won't compile.

    Read the full answer
  • 2.5 Which new String methods came in Java 11? Easy 1 min

    Java 11 added isBlank, which is true for empty or whitespace-only strings; lines, which gives a stream of lines; strip, stripLeading and stripTrailing, which remove Unicode whitespace; and repeat, which repeats a string n times. The key difference is strip versus the old trim: trim only removes characters up to the plain space code, while strip understands all Unicode whitespace.

    Read the full answer

Java 12 to 17

Switch expressions, text blocks, records, sealed classes, pattern matching for instanceof and Java 17 platform changes.

  • 3.1 What are switch expressions? Easy 1 min

    Since Java 14, switch can be an expression that returns a value. The arrow form has no fall-through, so no forgotten break. One case can list several labels, and for a block you return the value with yield. Because it must always produce a value, the compiler checks it is exhaustive, so for an enum you either cover every constant or add a default.

    Read the full answer
  • 3.2 What are text blocks? Easy 1 min

    Text blocks, final in Java 15, are multi-line string literals between three double quotes. You write JSON, SQL or HTML as it looks, without escaping every quote or joining lines with plus signs. The compiler removes the common indentation, so you can indent the block with your code. The result is a normal String, so you can call formatted on it to fill in values.

    Read the full answer
  • 3.3 What are records and when should you use them? Easy 1 min

    A record, final in Java 16, is a short way to declare a class that just carries data. From one line, the compiler generates private final fields, a constructor, accessor methods, and equals, hashCode and toString based on all the fields. I use them for DTOs, value objects, map keys and returning several values. Validation goes in a compact constructor.

    Read the full answer
  • 3.4 What are sealed classes? Medium 2 min

    Sealed classes, final in Java 17, let a class or interface decide exactly which types may extend it, using a permits list. Each allowed subtype must say whether it is final, sealed again, or non-sealed. This models a closed set of cases, like payment types. The big win is that the compiler can check a switch over a sealed type covers every case, without a default.

    Read the full answer
  • 3.5 What is pattern matching for instanceof? Easy 1 min

    Since Java 16, instanceof can test the type and bind a variable in one step, so I don't need a separate cast afterwards. For example, if obj is a String s, I can use s directly. The variable only exists where the compiler knows the match succeeded, so it also works after an and condition, or after an early return when the check fails.

    Read the full answer
  • 3.6 How is a record different from a traditional class? Medium 1 min

    A record's fields are always final and set through its constructor, and equals and hashCode always use all the components. A record can't extend another class, because it already extends Record, and it can't add extra instance fields. It can implement interfaces and have methods. Also, it is only shallowly immutable: a list inside can still change unless I copy it in the constructor.

    Read the full answer
  • 3.7 What JVM and platform improvements do you get by moving to Java 17? Medium 2 min

    Java 17 is a long-term support release, so coming from 8 or 11 you collect a lot. ZGC and Shenandoah became production-ready low-pause collectors in Java 15. NullPointerExceptions now say exactly which variable was null. JDK internals are strongly encapsulated, which is the usual upgrade blocker for old reflection-heavy libraries. You also get records, sealed classes, switch expressions and text blocks.

    Read the full answer

Java 18 to 21

Virtual threads, structured concurrency, scoped values, pattern matching for switch, sequenced collections and generational ZGC.

  • 4.1 What are virtual threads? Medium 1 min

    Virtual threads, final in Java 21, are lightweight threads managed by the JDK instead of the operating system. They are still normal Thread objects, so ordinary blocking code works. When a virtual thread waits on I/O, the JDK unmounts it from the real OS thread underneath, and that OS thread runs another virtual thread. So I can have one thread per request, even with a huge number of requests.

    Read the full answer
  • 4.2 How do virtual threads differ from platform threads? Medium 1 min

    A platform thread is a thin wrapper over one OS thread for its whole life. It's expensive, so we pool them. A virtual thread is cheap: you make one per task and never pool it. They don't make code faster; they let many more tasks wait at once, so they help throughput for I/O-heavy work, not CPU-heavy work. In Java 21, watch out for pinning inside synchronized blocks.

    Read the full answer
  • 4.3 How do virtual threads improve scalability? Hard 1 min

    For a server, the number of requests in progress equals throughput times latency; that's Little's Law, and JEP 444 uses it. With one blocking platform thread per request, the OS thread count caps how many requests can be in progress, so it caps throughput. Virtual threads remove that cap. I keep simple blocking code but can handle far more waiting requests, without rewriting everything in reactive style.

    Read the full answer
  • 4.4 What is structured concurrency? Hard 1 min

    Structured concurrency treats a group of subtasks as one unit of work. I open a scope, fork subtasks like fetching a user and their orders, then join. If one fails, the others are cancelled, and no subtask can outlive the scope, so no thread leaks. In Java 21 it is only a preview API, so it needs the enable-preview flag and isn't final yet.

    Read the full answer
  • 4.5 What are scoped values? Hard 1 min

    A scoped value shares read-only data, like the current user or request id, with every method called inside a bounded block, without passing it as a parameter. It's a safer ThreadLocal: callees can't change it, it disappears automatically when the block ends, and child tasks inherit it cheaply. It was a preview in Java 21 and became final later, in Java 25.

    Read the full answer
  • 4.6 What is pattern matching for switch? Medium 1 min

    Final in Java 21, switch cases can match on types instead of only constants, with guards using when, and a case for null. Combined with record patterns, also final in 21, a case can take a record apart into its components directly. Over a sealed type, the compiler checks every subtype is covered, so I get type-safe branching without instanceof chains or the visitor pattern.

    Read the full answer
  • 4.7 What are sequenced collections? Easy 1 min

    Java 21 added interfaces for collections with a defined order: sequenced collection, sequenced set and sequenced map. They give one common way to get the first and last element, add or remove at either end, and get a reversed view. Before this, getting the last element meant different code for a list, a deque or a linked hash set. Now it's just getLast.

    Read the full answer
  • 4.8 What is Generational ZGC? Hard 1 min

    ZGC is the low-pause collector that does nearly all its work while the application keeps running. Java 21 added a generational mode that keeps young and old objects apart, so it can collect short-lived objects often and cheaply. In Java 21 you have to turn it on with an extra flag next to the normal ZGC flag. Later releases made generational the default and then the only mode.

    Read the full answer

Advanced Java and JVM

Locks, atomics, ConcurrentHashMap, executors, concurrency bugs, garbage collection and JVM memory areas.

  • 5.1 What is the difference between synchronized and ReentrantLock? Medium 1 min

    Both give mutual exclusion and the same memory visibility guarantees. Synchronized is simpler and always releases the lock when the block ends, even on an exception. ReentrantLock gives more control: try to lock with a timeout, wait in a way that can be interrupted, optional fairness, and multiple conditions. The cost is that I must unlock in a finally block myself.

    Read the full answer
  • 5.2 What is the difference between volatile and AtomicInteger? Medium 2 min

    Volatile only guarantees visibility and ordering: every thread sees the latest write to that variable. But count plus plus is a read, an add and a write, so two threads can still lose updates with volatile. AtomicInteger makes the whole increment atomic using compare-and-set, without a lock. So I use volatile for a simple stop flag and atomics for counters; under heavy contention, LongAdder.

    Read the full answer
  • 5.3 How does ConcurrentHashMap work internally? Hard 2 min

    ConcurrentHashMap lets many threads read and write without locking the whole map. Reads usually don't block, and writes only lock a small part of the table, so writers on different keys rarely wait for each other. Methods like compute, computeIfAbsent and merge are atomic per key. Size is only an estimate during updates, iterators never throw concurrent modification errors, and null keys or values aren't allowed.

    Read the full answer
  • 5.4 What is the difference between ExecutorService and ForkJoinPool? Medium 2 min

    ExecutorService is the general interface for running tasks on a pool; the classic version, ThreadPoolExecutor, has workers that take tasks from one shared queue. ForkJoinPool is one implementation built for work stealing: each worker has its own queue, and idle workers steal tasks from busy ones. It fits divide-and-conquer and many small tasks. Parallel streams and default CompletableFuture async calls use its common pool.

    Read the full answer
  • 5.5 What are deadlock, starvation, livelock and race conditions? Medium 2 min

    A deadlock is when two threads each hold a lock the other needs, so both wait forever. Starvation is when one thread rarely gets a resource because others keep taking it. Livelock is when threads keep reacting to each other but make no progress. A race condition is when the result depends on timing, like two threads incrementing the same counter. Fixes: consistent lock order, timeouts, and atomic or concurrent classes.

    Read the full answer
  • 5.6 How does garbage collection work in the JVM? Medium 1 min

    The garbage collector finds objects that can no longer be reached from roots, like thread stacks and static fields, and frees their memory. Most HotSpot collectors are generational, because most objects die young: the young area is collected often and cheaply, and objects that survive long enough move to the old area. Collectors trade throughput, pause time and memory. G1 is the default on most server machines.

    Read the full answer
  • 5.7 How do G1 and ZGC compare? Hard 1 min

    G1, the default, splits the heap into regions and tries to meet a pause-time goal, two hundred milliseconds by default, while keeping good throughput. ZGC does almost all its work while the application runs, so pauses stay very short even on huge heaps, at the cost of some CPU and memory overhead. I'd keep G1 for general services and pick ZGC when tail latency really matters.

    Read the full answer
  • 5.8 What are the JVM memory areas? Medium 1 min

    Each thread has its own program counter, its own JVM stack with a frame per method call holding local variables, and a native method stack. All threads share the heap, where every object and array lives and which the garbage collector manages, and the method area, which holds class data and code. In HotSpot since Java 8, class metadata lives in native memory called Metaspace, not in the heap.

    Read the full answer

Production scenarios

High CPU, GC pauses, memory optimization, high concurrency and migrating from Java 8 to 21.

  • 6.1 How do you troubleshoot high CPU in a Java service? Hard 2 min

    First I confirm it's the Java process and check whether garbage collection is burning the CPU, using the GC logs. If not, I find the hot threads: take a few thread dumps a few seconds apart and see which stacks stay busy. Then I record a short Flight Recorder profile to find the hot methods. Typical causes are busy loops, retry storms, heavy regex or serialization, and excessive logging.

    Read the full answer
  • 6.2 How do you investigate frequent GC pauses? Hard 2 min

    I turn on GC logging and look at how often pauses happen, how long they are, whether they're young or full collections, and how much heap is still used after each one. Frequent young pauses usually mean a high allocation rate. Old-generation usage that keeps climbing after every collection suggests a leak. Full collections in G1 usually mean the heap is too small. Then I reduce allocation, resize, or consider ZGC.

    Read the full answer
  • 6.3 How do you optimize memory usage in a Java application? Medium 1 min

    I measure before changing anything: heap used after garbage collection, plus a heap dump and a class histogram to see what's really taking space. Then I fix leaks like unbounded caches, static maps, listeners that are never removed, and thread locals never cleared. I stream large data instead of loading it all, use leaner data structures, and size the heap from measured live data, including container limits.

    Read the full answer
  • 6.4 How would you handle thousands of concurrent API requests? Hard 2 min

    For mostly I/O-bound requests on Java 21, I'd use virtual threads, one per request, so threads stop being the bottleneck. Then I protect the real limits: bounded database connections, timeouts on every remote call, and a cap on concurrent calls to each downstream service. When overloaded, I reject or queue extra requests instead of falling over. Caching, batching and scaling out behind a load balancer cover the rest.

    Read the full answer
  • 6.5 How would you migrate an application from Java 8 to Java 21? Hard 2 min

    I'd upgrade build tools and libraries first, because old bytecode tools and frameworks are the usual blockers. Then I scan for use of internal JDK APIs, add back Java EE pieces like JAXB that were removed in Java 11, and fix reflection that strong encapsulation now blocks. I check removed JVM flags, run the full test suite, roll out gradually, and only then adopt new features.

    Read the full answer