Interview Practice

Theory then practice

Java

Language contracts, collections, concurrency and JVM diagnosis.

Theory chapters

Prerequisites: Read simple Java methods; start with the values chapter if new.

  1. Chapter 1Java values, references and object contracts

    Reason about objects without confusing references, identity and equality.

  2. Chapter 2Collections, generics and stream pipelines

    Choose APIs that make data flow and mutation explicit.

  3. Chapter 3Visibility, atomicity and concurrent work

    Identify the invariant and synchronization boundary before choosing an API.

  4. Chapter 4JVM memory, resources and diagnosis

    Use evidence to separate allocation, contention and dependency delays.

  5. Chapter 5Records, sealed types and pattern matching

    Model closed sets of data so the compiler checks every case for you.

Interview questions

Try answering aloud before opening an answer. Reading an answer is exposure, not proof of independent mastery. Save reflections in your mistake notebook.

20 of 20 questions

Beginner

Is Java pass-by-value or pass-by-reference?

Reveal answer and example

Java passes values. For an object variable, that value is a reference: a method can mutate the referenced object, but rebinding its parameter does not rebind the caller's variable.

Example: A method can add to the caller's list, but assigning parameter = new ArrayList does not replace that caller's list.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Describing Java as pass-by-reference obscures the distinction between mutation and reassignment.

Follow-up: What happens when the referenced object is immutable?

Beginner

How do equals and hashCode work together?

Reveal answer and example

Equal objects must have equal hash codes; unequal objects may collide. Override both for value-based keys and keep equality reflexive, symmetric, transitive and consistent.

Example: An immutable OrderId value can safely key a map.

Common mistake: Mutating a key's equality fields after insertion can make lookups fail.

Follow-up: How can inheritance break equals symmetry?

Beginner

Why is String immutable, and when use StringBuilder?

Reveal answer and example

Immutable strings can be safely shared and used as stable keys. A mutable builder avoids repeatedly copying a growing string inside a loop; it is not automatically safe for shared concurrent mutation.

Example: Accumulate a CSV line in one builder, then call toString at the boundary.

Common mistake: Repeated concatenation inside a growing loop may copy all earlier characters repeatedly.

Follow-up: How would you process Unicode code points rather than UTF-16 code units?

Beginner

Which Java collection fits a workload?

Reveal answer and example

Choose by required operations: ArrayList for indexed access, ArrayDeque for a stack/queue, HashMap for exact lookup and TreeMap for sorted navigation. Then consider null handling, iteration order and concurrency.

Example: A FIFO work queue needs poll/offer semantics, not arbitrary list indexing.

Common mistake: Selecting LinkedList solely because inserts are O(1) ignores lookup and allocation costs.

Follow-up: What would you measure before changing a hot collection?

Intermediate

What do extends and super mean in generic APIs?

Reveal answer and example

A producer of T can be accepted as ? extends T, while a destination consuming T can be ? super T. Generic types are invariant; List<Integer> is not a subtype of List<Number>.

Example: Copy numbers from a List<? extends Number> into a List<? super Number>.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: You generally cannot add arbitrary values to an extends wildcard because its actual element type is unknown.

Follow-up: How does type erasure affect overloads and runtime checks?

Beginner

Checked or unchecked exception: how do you choose?

Reveal answer and example

Checked exceptions make callers acknowledge a recoverable contract at compile time; unchecked exceptions often represent programming errors or domain failures handled at a boundary. Preserve causes and translate at meaningful layers.

Example: An API boundary maps a domain not-found exception to a documented response.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Catching Exception and returning success destroys failure visibility.

Follow-up: When would try-with-resources preserve suppressed exceptions?

Beginner

When should a method return Optional?

Reveal answer and example

Optional is designed as a return type that makes "no result" explicit for lookups such as findById. Callers then choose orElse, orElseThrow or map instead of forgetting a null check. It is not intended for fields, parameters or collections, where it adds allocation and awkward APIs; return an empty collection rather than Optional<List>.

Example: userRepository.findByEmail(email).map(User::id).orElseThrow(() -> new NotFound(email)).

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Calling optional.get() without checking, or using orElse(expensiveCall()) which evaluates eagerly; use orElseGet for lazy defaults.

Follow-up: Why might Optional be avoided in a hot loop that runs millions of times?

Beginner

What do records give you, and what do they not?

Reveal answer and example

A record declares components and gets a canonical constructor, accessors, equals, hashCode and toString derived from them. Fields are final, so records make good DTOs, map keys and value objects. They are only shallowly immutable, cannot extend classes and should not be used for JPA entities that need a no-argument constructor and mutable state.

Example: record Point(int x, int y) {} can be a HashMap key without hand-writing equals and hashCode.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Storing a mutable List in a record and assuming the record is immutable; copy it with List.copyOf in a compact constructor.

Follow-up: How would you validate that a record's amount is non-negative?

Intermediate

When are streams clearer than loops?

Reveal answer and example

Streams express transformations and reductions over a source. Keep operations free of shared side effects, understand laziness and short-circuiting, and use loops where control flow is clearer.

Example: Filter invalid orders and sum amounts without mutating an external accumulator.

Common mistake: Parallel streams do not guarantee lower latency and often use shared execution infrastructure.

Follow-up: Why must a reduction's combining operation be associative?

Intermediate

Does a final field make an object immutable?

Reveal answer and example

final prevents reassignment of that field after construction; it does not freeze a referenced list or array. Use encapsulation, defensive copies and immutable elements, and avoid leaking this during construction.

Example: A record containing a mutable List still needs an appropriate copy policy.

Common mistake: List.copyOf protects the list structure but does not deep-copy mutable elements.

Follow-up: What does safe publication guarantee for properly constructed final fields?

Advanced

volatile, synchronized or atomic variable?

Reveal answer and example

volatile provides visibility and ordering for a variable, but a read-modify-write increment is multiple actions. synchronized protects a critical section; atomics provide specific atomic operations such as compare-and-set.

Example: Use AtomicInteger.incrementAndGet for a shared counter rather than volatile count++.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Thread-safe individual fields do not automatically preserve a multi-field invariant.

Follow-up: How would you update two account balances atomically?

Advanced

Why is ConcurrentHashMap safer than sharing HashMap?

Reveal answer and example

It supports concurrent access with defined visibility guarantees. Use atomic per-key methods such as compute or merge for compound changes; arbitrary get-then-put sequences can still race.

Example: Use merge(key, 1, Integer::sum) for independent per-key counts.

Common mistake: A concurrent map does not make mutable values or a cross-key transfer atomic.

Follow-up: When should compute callbacks avoid blocking external work?

Intermediate

How do you size and bound an executor?

Reveal answer and example

Separate CPU work from waiting work, bound accepted backlog and define rejection behavior. Measure queue time and service time; unbounded queues can turn overload into memory exhaustion.

Example: Reject excess requests or apply backpressure rather than enqueueing indefinitely.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Increasing threads does not increase database connection capacity.

Follow-up: How would you choose a timeout budget across queued and running work?

Advanced

What do virtual threads solve and what do they not solve?

Reveal answer and example

They make many blocking tasks cheaper to represent, improving scalability for waiting-heavy workloads. They do not accelerate CPU-bound work or remove limits on connections, downstream services or shared locks.

Example: Use one virtual thread per blocking request while bounding scarce database access with a semaphore.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Java 21-era synchronized pinning advice is version-specific: JDK 24 removed that monitor limitation; native/foreign calls can still pin.

Follow-up: Which JDK version and blocking operations would you profile first?

Advanced

How do you compose asynchronous work safely?

Reveal answer and example

Use thenCompose for dependent asynchronous stages and thenCombine for independent results. Choose executors deliberately, propagate exceptions and define timeouts and cancellation ownership.

Example: Fetch independent inventory and price results before combining a quote.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: join inside an asynchronous pipeline can block a worker; a timeout does not necessarily cancel underlying I/O.

Follow-up: How would partial failure affect the response contract?

Intermediate

How do stack, heap and garbage collection relate?

Reveal answer and example

Each thread has execution frames; objects are generally managed on the heap, though optimizations can eliminate allocations. GC reclaims unreachable objects, so retained references can cause memory growth even without manual allocation leaks.

Example: A static map retaining every request prevents those request graphs from becoming unreachable.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Not every pause is GC; inspect allocation, threads, CPU and I/O evidence.

Follow-up: How would you distinguish a leak from an intentionally growing cache?

Intermediate

How does HashMap work internally, and what breaks it?

Reveal answer and example

HashMap stores entries in an array of buckets indexed by a spread hash of the key. Collisions chain in a bucket; since Java 8 a bucket with many entries can become a balanced tree when the table is large enough, bounding degenerate lookups to O(log n). When size exceeds capacity × load factor (0.75 by default) the table doubles and entries are redistributed.

Example: Pre-size with HashMap.newHashMap(expected) in Java 19+ to avoid repeated resizing when loading a known number of entries.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Mutating a key's fields after insertion changes its hash; the entry becomes unreachable even though it is still stored.

Follow-up: Why is HashMap unsafe under concurrent writes, and what does ConcurrentHashMap do differently?

Intermediate

How do sealed interfaces improve switch statements?

Reveal answer and example

A sealed interface lists permitted subtypes, so the compiler knows the full set of cases. A Java 21 pattern switch over it can be exhaustive without a default, and adding a new subtype causes compile errors at every unhandled switch. Record patterns let each case deconstruct its data directly.

Example: switch (result) { case Success(var value) -> ...; case Failure(var error) -> ...; } with no default branch.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Adding default -> to silence the compiler, which hides newly added cases until they fail at runtime.

Follow-up: When would you prefer polymorphic methods on the subtypes instead of a switch?

Advanced

How would you diagnose a slow Java service?

Reveal answer and example

Start from latency distributions and traces, then inspect CPU samples, thread states, allocation and dependency timings. Form a hypothesis and measure before changing pool sizes or GC flags.

Example: A request blocked on a database connection needs different treatment from a CPU-hot serialization loop.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Optimizing a microbenchmark that excludes the bottleneck may not improve request latency.

Follow-up: What production-safe evidence would you capture during an incident?

Advanced

How would you choose between G1, ZGC and Parallel GC?

Reveal answer and example

G1 is the default and balances throughput and pause targets for most heaps. ZGC performs almost all work concurrently and keeps pauses in the low milliseconds even for large heaps, at some throughput and memory cost; generational ZGC is the default ZGC mode from JDK 23. Parallel GC maximizes throughput for batch jobs that tolerate longer pauses. Measure with GC logs before and after.

Example: A latency-sensitive API with a 32 GB heap and p99.9 pauses above 200 ms under G1 is a candidate for ZGC; a nightly batch job may prefer Parallel.

Big O is not intrinsic to this conceptual question. Define the implementation, input size and operation before assigning a complexity.

Common mistake: Tuning flags before checking allocation rate and live-set size; excessive allocation or a leak is not fixed by changing collectors.

Follow-up: Which GC log fields would you inspect to confirm the change helped?

Reference documentation