Skip to content

Java Developer interview questions

100 real questions with model answers and explanations for Middle candidates.

See a Java Developer resume example

Practice with flashcards

Spaced repetition · Hunter Pass

Questions

concurrencysystem-design

A thread is an execution path inside a process and shares that process's heap and resources with other threads.

  • Each process has an isolated address space, while threads in one JVM can access the same objects.
  • Every thread has its own stack, program counter, and call frames despite sharing the heap.
  • Shared memory makes communication cheap but requires synchronization when mutable state is accessed concurrently.

Why interviewers ask this: The interviewer evaluates whether you connect the basic execution model to Java's shared-state concurrency risks.

concurrency

start() asks the JVM to create a new execution path, while run() is an ordinary method call on the current thread.

  • start() eventually invokes run() on the new thread after the scheduler makes it runnable.
  • Calling run() directly provides no concurrency and blocks the caller until the method returns.
  • A Thread instance can be started only once, and a second start() throws IllegalThreadStateException.

Why interviewers ask this: A strong answer distinguishes thread creation from method invocation and knows the one-start lifecycle rule.

concurrency

Thread.State describes where a thread is in its lifecycle rather than whether it is currently using a CPU core.

  • A new thread moves from NEW to RUNNABLE after start(), and reaches TERMINATED when run() completes or fails.
  • Entering a contended synchronized block produces BLOCKED, while Object.wait() or an unbounded join() produces WAITING.
  • sleep(), a timed wait(), and a timed join() produce TIMED_WAITING until time expires or another wake-up condition occurs.

Why interviewers ask this: The interviewer checks whether you can distinguish lock contention from waiting and timed waiting.

concurrency

A race condition occurs when a result depends on an uncontrolled interleaving of concurrent operations.

  • count++ is a read-modify-write sequence, not one indivisible operation.
  • Two threads can read the same old value, calculate the same increment, and overwrite one result.
  • Protect the operation with a common lock or use an atomic counter designed for concurrent updates.

Why interviewers ask this: The interviewer wants evidence that you reason about compound operations rather than assuming one source-code expression is atomic.

concurrency

synchronized provides mutual exclusion and memory visibility for code guarded by the same monitor.

  • Only one thread at a time can execute a critical section protected by a particular monitor.
  • Releasing the monitor flushes prior writes, and a later acquisition of that monitor makes those writes visible.
  • Java monitors are reentrant, so a thread holding a monitor can enter another block guarded by it without deadlocking itself.

Why interviewers ask this: A complete answer covers both locking and the happens-before effect instead of describing synchronized as only a mutex.

oopconcurrencymodifiers

The monitor depends on the object named implicitly or explicitly by the synchronization form.

  • An instance synchronized method locks this, so calls on different instances do not exclude each other.
  • A static synchronized method locks the Class object, which is shared by all instances loaded by that class loader.
  • A synchronized block locks its expression, allowing a private dedicated lock and a smaller critical section.

Why interviewers ask this: The interviewer tests whether you can identify the actual lock and avoid accidental use of unrelated monitors.

wait and notification coordinate through an object's monitor, while sleep only pauses the current thread.

  • wait() must be called while holding that monitor and releases it until the thread is awakened and reacquires it.
  • notify() wakes one arbitrary waiter and notifyAll() wakes all waiters, which must then compete for the monitor.
  • sleep() neither requires nor releases a monitor, so sleeping inside synchronized can block other threads unnecessarily.
  • A wait condition belongs in a while loop because wake-ups can be spurious or the condition can change before reacquisition.

Why interviewers ask this: A strong answer knows monitor ownership, lock release behavior, and the need to recheck a condition.

concurrencymemory

volatile gives visibility and ordering guarantees for reads and writes of one variable, but it does not make compound actions atomic.

  • A write to a volatile variable happens-before every subsequent read of that same variable.
  • The JVM and CPU cannot reorder relevant memory operations across the volatile access in ways that break this guarantee.
  • It is suitable for state flags or safely publishing an immutable value when updates do not depend on the old value.

Why interviewers ask this: The interviewer checks whether you separate visibility and ordering from mutual exclusion and atomicity.

concurrency

volatile makes each read and write visible, but the increment still consists of multiple operations that can interleave.

  • Each thread can observe the latest counter and still lose an update between its read and write.
  • AtomicInteger.incrementAndGet() performs the update atomically, usually with compare-and-set.
  • synchronized or Lock is preferable when the invariant spans several variables or several operations.

Why interviewers ask this: The interviewer evaluates whether you choose a synchronization mechanism based on the full invariant rather than one field.

memoryconcurrency

Happens-before is the rule that makes one action's effects guaranteed to be visible to another action.

  • Program order establishes it between earlier and later actions within one thread.
  • A monitor unlock happens-before a later lock of that monitor, and a volatile write happens-before a later read of that variable.
  • Thread.start() publishes prior actions to the started thread, while a successful join() publishes the finished thread's actions to the joiner.
  • Without a happens-before path, observing another thread's writes is not guaranteed even if tests usually appear to work.

Why interviewers ask this: A strong answer can name concrete ordering edges and explain why wall-clock order alone is insufficient.

Safe publication makes a fully constructed object's state reliably visible to every thread that obtains its reference.

  • Publication through a volatile field, a synchronized handoff, a static initializer, or a concurrent collection creates the required ordering.
  • Properly constructed final fields receive special visibility guarantees, but this does not make mutable referenced objects immutable.
  • Leaking this from a constructor can expose default or partially initialized state before construction finishes.

Why interviewers ask this: The interviewer tests whether you understand that building an object correctly is not enough if its reference is shared unsafely.

jvmconcurrency

The compiler, JVM, and processor may reorder operations for performance as long as single-thread behavior remains valid.

  • Another thread can observe writes in an unexpected order when the threads have a data race.
  • synchronized, volatile, thread lifecycle rules, and concurrent utilities introduce ordering constraints recognized by the memory model.
  • Timing assumptions or adding sleep() do not establish visibility and cannot replace a happens-before relationship.

Why interviewers ask this: The interviewer evaluates whether you reason from Java Memory Model guarantees instead of apparent execution order.

lockingconcurrency

Deadlock requires exclusive resources, hold-and-wait, no forced preemption, and a cycle of dependencies.

  • Code that acquires multiple locks in inconsistent order can create the circular wait.
  • A global lock order or a single lock around one invariant removes that cycle.
  • tryLock with a timeout can let code abandon and retry acquisition, but the failure path must release every acquired lock.

Why interviewers ask this: A strong answer identifies the underlying conditions and offers prevention techniques rather than only defining deadlock.

lockingconcurrency

Starvation denies one thread progress, while livelock keeps threads active but prevents useful progress.

  • Starvation can arise when a thread repeatedly loses access to a non-fair lock or executor capacity.
  • In livelock, threads respond to each other by repeatedly yielding or retrying in a synchronized pattern.
  • Deadlocked threads are blocked in a dependency cycle, whereas starving or livelocked threads may still change state.

Why interviewers ask this: The interviewer checks whether you distinguish several liveness failures that need different remedies.

concurrency

Lock is useful when code needs acquisition behavior that the synchronized statement does not expose.

  • tryLock supports immediate or timed failure, and lockInterruptibly allows cancellation while waiting.
  • A Lock can provide multiple Condition queues, whereas an object's monitor has one wait set.
  • synchronized is usually simpler for lexical critical sections because monitor release is automatic even when an exception occurs.
  • A Lock must always be released in a finally block after successful acquisition.

Why interviewers ask this: The interviewer evaluates whether you recognize Lock's capabilities without replacing simpler synchronized code by habit.

ReentrantLock is an explicit reentrant mutex with interruptible, timed, and observable acquisition operations.

  • Reentrancy tracks a hold count, so the owner must unlock as many times as it successfully locked.
  • A fair lock generally grants access to the longest-waiting thread under contention.
  • Fairness can reduce starvation but often lowers throughput, and untimed tryLock may still bypass the fairness queue.

Why interviewers ask this: A strong answer explains both the API advantages and the throughput trade-off of fairness.

ReadWriteLock can improve concurrency when reads are frequent, writes are rare, and protected work is substantial.

  • Multiple readers may hold the read lock together, but the write lock requires exclusive access.
  • Lock management and contention overhead can make a plain mutex faster for short sections or frequent writes.
  • ReentrantReadWriteLock does not support a straightforward read-to-write upgrade, which can deadlock if attempted while retaining the read lock.

Why interviewers ask this: The interviewer checks whether you evaluate workload shape and upgrade hazards instead of assuming more locks mean more speed.

concurrency

Atomic classes update a value by comparing its current state with an expected state and conditionally replacing it.

  • A failed compare-and-set indicates interference, so lock-free algorithms usually reread the value and retry.
  • The operation is atomic and has volatile-style visibility semantics without taking a Java monitor.
  • ABA occurs when a value changes from A to B and back to A, so a comparison sees A but misses the intervening change.
  • AtomicStampedReference or AtomicMarkableReference can attach version information when that history matters.

Why interviewers ask this: A strong answer goes beyond naming CAS and recognizes a correctness limitation of value-only comparison.

concurrency

AtomicLong maintains one immediately readable atomic value, while LongAdder spreads contended updates across internal cells.

  • AtomicLong is appropriate when updates need compare-and-set semantics or every read must be a single linearizable value.
  • LongAdder usually scales better for heavily updated metrics because threads contend on different cells.
  • LongAdder.sum() combines cells and is not an atomic snapshot relative to concurrent updates, so it is unsuitable for coordination logic.

Why interviewers ask this: The interviewer evaluates whether you trade exact atomic observation against update throughput correctly.

concurrency

ExecutorService separates task submission from thread creation, reuse, scheduling, and shutdown policy.

  • Reusing a bounded set of workers avoids the memory and context-switch cost of one thread per task.
  • The executor can queue work, reject overload, return Future results, and expose lifecycle operations.
  • The application still has to choose queue capacity, pool size, thread factory, and rejection behavior for its workload.

Why interviewers ask this: The interviewer checks whether you see an executor as a resource-management policy rather than only convenient syntax.

Locked questions

  • 21

    How should you reason about thread-pool size for CPU-bound and I/O-bound work?

    concurrency
  • 22

    How do execute() and submit() differ on ExecutorService?

    concurrency
  • 23

    How should an ExecutorService be shut down correctly?

    concurrency
  • 24

    How do Future and CompletableFuture differ?

    concurrency
  • 25

    How does ForkJoinPool's work-stealing model operate?

  • 26

    Why are Java generic types invariant?

    generics
  • 27

    What does the PECS rule mean for bounded wildcards?

    generics
  • 28

    When should a generic method use a type parameter instead of a wildcard?

    generics
  • 29

    How do bounded type parameters and intersection bounds work in Java?

  • 30

    What is type erasure, and what limitations does it impose on Java generics?

    generics
  • 31

    What are raw types and heap pollution?

    data-structuresgenericsmemory
  • 32

    Why can you create String[] but not new List<String>[] in Java?

    strings
  • 33

    What makes an interface functional, and how are lambdas represented conceptually?

    typeslambdalambdas
  • 34

    Why may a lambda capture only final or effectively final local variables?

    lambdalambdasmodifiers
  • 35

    How do intermediate and terminal Stream operations differ?

  • 36

    What is the difference between map() and flatMap() in the Stream API?

    streams
  • 37

    When should you use reduce() versus collect() on a Stream?

  • 38

    Why are side effects risky in stream pipelines, especially parallel ones?

    ci-cd
  • 39

    How do encounter order and short-circuiting affect Stream execution?

  • 40

    How does HashMap locate a value by key in modern Java?

    collections
  • 41

    What contract must equals() and hashCode() satisfy for hash-based collections?

    collectionsequals-hashcode
  • 42

    What happens when HashMap resizes, and how do capacity and load factor affect it?

    capacitycollections
  • 43

    How does ConcurrentHashMap support concurrent access, and how does it differ from HashMap?

    concurrencycollections
  • 44

    How do fail-fast, snapshot, and weakly consistent iterators differ?

    snapshotiteration
  • 45

    How does the JVM determine whether an object is eligible for garbage collection?

    gcjvmcollections
  • 46

    Why do JVM collectors use generations, and what are minor and major collections?

    jvmcollections
  • 47

    How do the Strategy and Template Method patterns differ?

  • 48

    When are Factory Method and Builder useful, and what problem does each solve?

  • 49

    What are IoC and dependency injection in Spring Core?

    springinjectiondependencies
  • 50

    How does Spring create and initialize a singleton bean, and where can dependency injection fail?

    springinjectiondependencies
  • 51

    Two requests occasionally overwrite each other's changes to the same in-memory object. How would you diagnose and fix the race condition?

    memoryconcurrency
  • 52

    A Spring service stops responding under load while CPU remains low and all request threads are waiting. What would you investigate?

    concurrencydebuggingspring
  • 53

    Production thread dumps show two Java threads waiting forever for locks held by each other. How would you resolve this deadlock?

    concurrencylocking
  • 54

    After adding synchronization, correctness improves but throughput collapses. How would you reduce lock contention safely?

    throughput
  • 55

    A CompletableFuture chain sometimes hangs and sometimes hides the original exception. How would you make it reliable?

    error-handlingconcurrencyexceptions
  • 56

    A cache implemented with HashMap occasionally corrupts data during concurrent access. How would you replace it?

    concurrencycollectionscaching
  • 57

    Would you switch a blocking Spring Boot service to Java virtual threads to handle more concurrent requests?

    concurrencyspring
  • 58

    A Spring Boot process grows until it throws OutOfMemoryError after several days. How would you find the leak?

    springconcurrency
  • 59

    A Java service suddenly consumes a full CPU core with no traffic increase. What is your debugging sequence?

  • 60

    Users report periodic latency spikes that align with long JVM garbage collection pauses. How would you improve the situation?

    latencygcjvm
  • 61

    One REST endpoint has poor p99 latency, but its average latency is normal. How would you locate the slow segment?

    restendpointslatency
  • 62

    A Spring Data endpoint issues hundreds of queries when returning a page of orders. How would you fix it without breaking pagination?

    endpointspaginationqueries
  • 63

    A PostgreSQL query used by Hibernate becomes slow as the table grows. How would you optimize it?

    queriespostgresorm
  • 64

    A nightly Java batch job loads ten million rows into memory and crashes. How would you redesign it?

    batchmemory
  • 65

    You add Redis caching to a read-heavy product endpoint. How would you prevent stale or dangerous cache behavior?

    cachingendpointsredis
  • 66

    Requests time out because the HikariCP connection pool is exhausted. What would you do?

    pooling
  • 67

    A Spring service class has grown to 2,000 lines and mixes validation, persistence, mapping, and external calls. How would you refactor it safely?

    refactoringvalidationspring
  • 68

    A legacy Java module spreads null checks and NullPointerException handling across every caller. How would you improve it?

    spreadfundamentals
  • 69

    A Stream API pipeline is concise but slow and hard to debug on a hot path. Would you keep it?

    ci-cdapistreams
  • 70

    Controllers duplicate try-catch blocks and return inconsistent error bodies. How would you refactor the Spring REST layer?

    refactoringrestspring
  • 71

    You need to extract one module from a Spring monolith, but its tables and services are tightly coupled. What is your approach?

    monolithspring
  • 72

    A JUnit test passes locally but fails intermittently in CI. How would you debug and stabilize it?

    unitjunit
  • 73

    How would you unit-test a Spring service that calculates a price, saves an order, and sends a notification?

    hypothesis-testingspring
  • 74

    A Mockito test verifies every getter and exact call order, then breaks during harmless refactoring. How would you improve it?

    refactoringmocking
  • 75

    When would you replace mocked repository tests with a real database integration test?

    integrationdatabase
  • 76

    A pricing method has many input combinations and edge cases. How would you cover it with JUnit 5 without duplicating tests?

    junitpricing
  • 77

    A Spring integration test passes because each test rolls back, but production fails at transaction commit. How would you expose the real behavior?

    transactionsintegrationspring
  • 78

    A Spring method launches asynchronous work and returns immediately. How would you test it without Thread.sleep?

    asyncconcurrencyspring
  • 79

    Clients retry POST /orders after network timeouts and sometimes create duplicate orders. How would you fix the API?

    resilienceapi
  • 80

    How would you design validation and error responses for a Spring REST endpoint that creates a customer?

    designspringrest
  • 81

    You must change a widely used REST response without breaking existing clients. What is your rollout plan?

    rest
  • 82

    An events API becomes slow and skips records while users paginate through frequently changing data. How would you redesign pagination?

    paginationrecords
  • 83

    Two users edit the same product and the last save silently wins. How would you prevent lost updates with Spring Data JPA?

    ormspring
  • 84

    A money transfer reads two balances and updates them in one Spring transaction. What concurrency risks would you address?

    concurrencytransactionsspring
  • 85

    A method annotated with @Transactional is called from another method in the same Spring bean, but no transaction starts. How would you fix it?

    transactionsspring
  • 86

    A Spring transaction saves an order and then publishes a Kafka event, but crashes can leave the database and broker inconsistent. How would you fix it?

    databasetransactionskafka
  • 87

    A REST endpoint depends on a slow external payment API. How would you stop that dependency from exhausting your service?

    restendpointsdependencies
  • 88

    Spring reports a circular dependency between two services after a feature change. How would you resolve it?

    springdependencies
  • 89

    A Spring Security API returns 403 for expired tokens and 401 for authenticated users lacking a role. How would you correct it?

    tokensapispring
  • 90

    How would you implement a large file upload endpoint in Spring Boot without exhausting memory or exposing the service?

    springendpointsmemory
  • 91

    A Kafka consumer occasionally applies the same event twice after a restart. How would you make processing safe?

    kafkaconcurrency
  • 92

    One malformed Kafka message is retried forever and blocks its partition. How would you handle it?

    partitioningkafka
  • 93

    For communication between two internal Java services, how would you decide between REST and gRPC?

    restgrpc
  • 94

    Kubernetes terminates a Spring Boot pod during deployment and some requests fail. How would you implement graceful shutdown?

    lifecyclespringkubernetes
  • 95

    How would you configure Spring Boot health checks so Kubernetes does not restart a healthy but temporarily dependent service?

    kubernetesconfighealth-checks
  • 96

    A Java container is killed for exceeding its memory limit even though the configured heap is below that limit. What would you change?

    memorycontainersconfig
  • 97

    After enabling parallel JUnit execution in CI, tests become faster but start interfering with each other. How would you keep the speedup?

    junit
  • 98

    During an incident you discover access tokens and customer data in Spring application logs. How would you fix the logging path?

    incidentsloggingtokens
  • 99

    A pull request replaces readable code with a complex cache and claims a 10x speedup from a microbenchmark. How would you review it?

    code-reviewcachingcapacity
  • 100

    A new release doubles error rate and latency in a Java service. How would you handle the production incident and follow-up?

    incidentslatency