In this article
Language and memory
Why does == on two Integer objects give true for 127 but false for 128?
The == operator compares object identity, and only some boxed values are certain to be shared. The language specification says constant int values from -128 to 127 always box to one shared object, and allows no assumption outside that range (language specification, section 5.1.7, Java 21). On a default Java Virtual Machine (JVM) our run gave true for 127 and false for 128; with -XX:AutoBoxCacheMax=1000, 128 also gave true.1 Compare boxed values with equals.
Why it is asked: it separates people who know identity from equality. Wider context: coding interview preparation. Read the full explanation: the equals and hashCode contract.
Why must you override hashCode whenever you override equals?
Hash-based collections find an object by its hash code first, so two equal objects must produce the same one. The Java application programming interface (API) documentation states that equal objects must have equal hash codes and that overriding equals generally means overriding hashCode (Object, Java 21 documentation). In our run a HashSet kept 2 equal objects when only equals was overridden and 1 when both were.1
Why it is asked: broken contracts cause silent data bugs that a lead must catch in review. Read the full explanation: the equals and hashCode contract.
What happens if you change a field that is part of a HashMap key?
The entry is lost to lookups but stays in the map. HashMap saved the key's old hash code with the entry, so a lookup with the changed key fails the hash check, and a lookup with a fresh copy of the old key matches the hash but fails equals against the changed key. In our run both lookups returned null, yet the map still held 1 entry that iteration returned, so it leaks until removed through an iterator.1 Use immutable keys.
Why it is asked: it tests whether you understand how hashing and mutability interact. Read the full explanation: how HashMap finds a key.
Why are Java strings immutable, and what does that buy you?
A String never changes after creation, so calling concat returns a new string and leaves the original alone; our run printed abc after an unassigned concat("def").1 Immutability makes strings safe to share between threads without locks, safe as map keys, and cacheable. The cost is allocation when you build long strings in a loop, where a StringBuilder is the right tool.
Why it is asked: it checks that you can reason about thread safety from design, not from locks. Read the full explanation: Java strings and equality.
What is garbage collection, and how do you choose a collector?
Garbage collection is the automatic management of a Java program's dynamic memory by the JVM, which frees objects the program can no longer reach. The Oracle tuning guide says Garbage-First (G1) is selected by default on most hardware and recommends it when response time matters more than throughput, describes the parallel collector for medium to large data sets, and says the Z Garbage Collector (ZGC) targets low latency at the cost of some throughput (Garbage Collection Tuning Guide, Java 21). Measure pauses on real traffic first.
Why it is asked: a lead owns production behaviour, and tuning without measuring is a classic mistake. Read the full explanation: system design interview preparation.
How do you remove items from a list while iterating over it?
Do not call remove inside a for-each loop. The list usually detects the structural change and throws ConcurrentModificationException, which our run confirmed.1 Use removeIf with a condition, or an Iterator and its own remove method. In our run removeIf on the list 1, 2, 3 with a condition for 1 left 2 and 3.1
Why it is asked: it is a small bug that shows up in real code reviews. Read the full explanation: removing inside a for-each loop.
Concurrency
What does volatile ensure, and what does it not?
The volatile keyword ensures visibility and ordering, not atomicity of compound actions. The specification says a write to a volatile field happens-before every subsequent read of that field (language specification, chapter 17, Java 21). Happens-before is an ordering relationship that makes the first action visible to, and ordered before, the second. A count++ on a volatile field is still a read, an add and a write, so two threads can lose updates.
Why it is asked: confusing visibility with atomicity is the most common concurrency error. Read the full explanation: synchronized versus volatile.
What is a data race, and how do you avoid one?
A data race is two conflicting accesses to the same variable, at least one a write, that are not ordered by happens-before (language specification, section 17.4.5). Avoid it by not sharing mutable state, by making state immutable, or by ordering every access with synchronized, a lock, a volatile variable or an atomic class. Tools can find races, but design that avoids shared state is better.
Why it is asked: it tests whether you can name the problem precisely, not just say "thread safety". Read the full explanation: race conditions and atomicity.
When would you use synchronized, an atomic class or volatile?
Use volatile for a flag that one thread writes and others read, an atomic class for a single counter or reference, and synchronized or a lock when several variables must change together. In our run two threads each adding 100,000 times gave 200,000 with both an AtomicInteger and a synchronized block.1 The figure below summarises the choice.
Text description of the diagram
- Volatile gives visibility, not atomicity.
- An atomic class suits a single counter or reference.
- Synchronized or a lock suits several variables that change together.
Why it is asked: the choice shows whether you pick the lightest correct tool. Read the full explanation: compare-and-swap versus a lock.
How does ConcurrentHashMap differ from a synchronized HashMap?
ConcurrentHashMap lets reads proceed without blocking while updates happen. Its documentation says retrieval operations generally do not block, null keys and values are not allowed, and iterators are weakly consistent and never throw ConcurrentModificationException (ConcurrentHashMap, Java 21 documentation). Our run threw NullPointerException for a null key.1 Collections.synchronizedMap locks the whole map on every call, allows null, and requires you to synchronize manually while iterating. A compound check-then-act still needs an atomic method such as computeIfAbsent.
Why it is asked: it tests your knowledge of how the standard library behaves under concurrency. Read the full explanation: HashMap versus ConcurrentHashMap.
What are virtual threads, and when should you use them?
A virtual thread is an instance of java.lang.Thread that is not tied to a particular operating-system thread. Java 21 finalised them in Java Development Kit (JDK) Enhancement Proposal (JEP) 444, which says they are not faster threads: they exist to provide scale, meaning higher throughput, not lower latency, and they should never be pooled (JEP 444, last updated October 2025). They suit tasks that mostly wait on input or output. Use a semaphore, not a small pool, to limit access to a scarce resource. In Java 21 a virtual thread is pinned while inside synchronized.
Why it is asked: it checks that you know what recent Java releases changed and what they did not. Read the full explanation: virtual threads in modern Java.
How do you size a thread pool?
Start from the kind of work, then measure. Work that mostly computes needs about as many threads as processor cores, while work that mostly waits on a network or a database tolerates more. Set a bounded queue so overload shows up as rejected work rather than memory exhaustion, then load-test with realistic traffic and watch queue length and latency, not only utilisation. For very many waiting tasks, consider virtual threads instead.
Why it is asked: it separates measurement from folklore. Read the full explanation: bounded queues and worker pools.
Knowing it is half the job. Saying it is the other half.
Interview Clozo is an AI interview copilot for real interview rounds: it hears the question and puts a strong answer on your screen, and you say it in your own words. See the features or how it works.
Start Free Trial →Design and architecture
How do you evolve a public API without breaking its users?
Add behaviour, do not change it, and signal changes with version numbers. Semantic Versioning 2.0.0 says to raise the major version for incompatible changes to the public API, the minor version for backward-compatible additions or deprecations, and the patch version for backward-compatible bug fixes (the Semantic Versioning 2.0.0 text). Deprecate first, document the replacement, give a stated period, then remove in a major release.
Why it is asked: a lead owns the contract between teams. Read the full explanation: API design interview questions.
Would you start a new system as a monolith or as microservices?
Usually as a single application with clean internal modules. Martin Fowler argued in 2015 for building a new application as a monolith first unless the team already has experience running microservices (MonolithFirst, June 2015); Stefan Tilkov argued the opposite on the same site (Don't start with a monolith, June 2015), and Fowler calls the advice tentative. Split along a boundary you have already seen cause pain, such as an independently scaling part or a separate team.
Why it is asked: it tests judgement over fashion. Read the full explanation: engineering management interviews.
How do you record an architecture decision so it survives staff turnover?
Write an architecture decision record. An architecture decision record is a short document that captures a single architectural decision and its rationale, as the Architectural Decision Records community site describes it. Keep the context, the options considered, the choice, and the consequences, store it beside the code, and never edit the past: supersede it with a new record.
Why it is asked: it shows that you make decisions durable, not only correct.
How do you handle technical debt on a team with feature deadlines?
Make it visible and priced. List the debt items with their cost to the team, such as extra days per release or incidents per quarter, and agree with the product owner a fixed share of each cycle for the highest-cost items. Pay debt that sits on the path of upcoming features first. Avoid a "rewrite" proposal without a measured outcome.
Why it is asked: it tests negotiation as much as engineering. Read the full explanation: tech debt versus features.
Leading the team
What is your standard for approving a code review?
Approve a change once it clearly improves the overall code health of the system, even if it is not perfect. Google's published review standard says exactly this: reviewers should favour approval when the change definitely improves overall code health (Google Engineering Practices). Block for correctness, security, and maintainability, label style comments as optional, and respond to reviews within a business day so work does not stall (Google, speed of code reviews).
Why it is asked: it reveals how you balance quality against speed. Read the full explanation: code review interview questions.
How do you measure whether your team is delivering well?
Use a few outcome measures and read them as a set. The DevOps Research and Assessment (DORA) programme lists change lead time, deployment frequency, failed deployment recovery time, change fail rate and deployment rework rate (DORA, last updated 5 January 2026). Pair speed with stability, compare a team with its own past, and never rank individual developers by these numbers.
Why it is asked: lead roles are judged on team outcomes. Read the full explanation: engineering management interviews.
How do you mentor a developer who keeps making the same mistake?
Find the cause before the cure. Ask the developer to explain their approach, then check whether it is a knowledge gap, an unclear standard or a habit. Agree one specific behaviour to change, pair on a real task, and give feedback within a day. Review progress after a few weeks and be explicit about expectations if the pattern continues.
Why it is asked: it is a behavioural question about growing people. Read the full explanation: behavioural interviews.
Tell me about a technical disagreement you resolved.
Use a short story: the decision, the two positions, how you gathered evidence, and the outcome. For example, two options for a cache were compared by a time-boxed prototype with a shared benchmark, and the team accepted the result. Say what you learned, and credit the other person's argument where it was right.
Why it is asked: leads settle disagreements without using rank. Read the full explanation: how to answer a conflict question; to prepare your own, see questions to ask an interviewer.
Follow-up questions
Does removing an element inside a for-each loop always throw?
No. Fail-fast iterators throw ConcurrentModificationException only on a best-effort basis (ArrayList, Java 21 documentation); in our run, removing 2 from the list 1, 2, 3 ended the loop with no exception and left 1 and 3.1
Do virtual threads still get pinned inside synchronized?
In Java 21, yes. JEP 444 says a virtual thread is pinned to its carrier while it runs a synchronized block or method, which can limit scale; JDK 24 removed that pinning (JEP 491).
What would make you split the monolith?
A boundary you have already seen cause pain: a part that must scale independently, or a team that keeps blocking another over shared code. Name the measured symptom first, then split along that line. See how interview loops are structured for where this question usually appears.
- Compare boxed values with equals, and override hashCode whenever you override equals.
- Volatile gives visibility, not atomicity: use an atomic class or a lock for compound updates.
- Virtual threads add scale, not speed, and should not be pooled.
- A lead owns contracts and decisions: version public APIs and record architecture decisions.
- Judge a team by outcome measures such as the DORA metrics, never by ranking individuals.
Sources
- The Java Language Specification, Java SE 21 Edition, chapters 5 and 17 — Oracle (JLS Java SE 21 index page; read 2026-10-24), 23 Aug 2023
- The Java Language Specification, Java SE 21 Edition, chapter 5 (boxing conversion) — Oracle (JLS Java SE 21; read 2026-10-24), 23 Aug 2023
- Object, Java SE 21 and JDK 21 API documentation — Oracle (page undated; read 2026-10-24), 24 Oct 2026
- ConcurrentHashMap, Java SE 21 and JDK 21 API documentation — Oracle (page undated; read 2026-10-24), 24 Oct 2026
- JEP 444: Virtual Threads — OpenJDK, 30 Oct 2025
- Garbage Collection Tuning Guide, JDK 21: Introduction and Available Collectors — Oracle (page undated; read 2026-10-24), 24 Oct 2026
- Semantic Versioning 2.0.0 — semver.org (specification version 2.0.0; page undated; read 2026-10-24), 24 Oct 2026
- MonolithFirst, Martin Fowler — martinfowler.com, 3 Jun 2015
- Architectural Decision Records — adr.github.io (latest dated item on the page; read 2026-10-24), 10 Mar 2026
- Don't start with a monolith, Stefan Tilkov — martinfowler.com, 9 Jun 2015
- JEP 491: Synchronize Virtual Threads without Pinning — OpenJDK, 27 Nov 2025
- ArrayList, Java SE 21 and JDK 21 API documentation — Oracle (page undated; read 2026-10-24), 24 Oct 2026
- Speed of Code Reviews, Google Engineering Practices — Google (page undated; read 2026-10-24), 24 Oct 2026
- DORA's software delivery performance metrics — DORA (DevOps Research and Assessment), 5 Jan 2026
- The Standard of Code Review, Google Engineering Practices — Google (page undated; read 2026-10-24), 24 Oct 2026