Java Interview Questions for Experienced Backend Developers (With Answers) | OfferBull

Java Interview Questions for Experienced Backend Developers (With Answers)
You’ve been shipping Java services for years. Then someone on a video call asks why a HashMap lookup returned null for a key you definitely put in, and you hear yourself say “something with hashing?”
That’s the trap with Java interview questions for experienced developers. You know this stuff in your hands. You’ve fixed the bug. But explaining it out loud, in order, while a stranger takes notes is a different skill, and most people never practice it.
Below: what interviewers ask once you’re past junior level, from collections and modern Java to concurrency, JVM memory and the backend scenarios where senior interviews usually get decided. Each section has questions, short spoken-style answers, and a couple of longer STAR stories.
The sample answers are illustrative. Names and numbers are made up. Version details come from the OpenJDK project pages and Oracle’s Java SE support roadmap as of October 2026, so check them against the JDK your target company actually runs.
What changes when you’re “experienced”
Junior Java interviews ask what something is. Experienced ones ask why you’d pick it, what it costs, and when it hurt you.
So “What is a ConcurrentHashMap?” turns into “A cache is shared by 200 request threads. What do you use, and what can still go wrong?” They assume you know the definition. They’re listening for trade-offs, and for the kind of detail only someone who’s been paged would know.
Expect a coding round too; seniority rarely gets you out of it. For the wider picture of a backend loop, including system design and API rounds, see our guide on how to prepare for backend engineering interviews.
Core Java and collections questions
These sound basic. They aren’t, at this level, because the follow-ups go deep fast.
- How do
equals()andhashCode()work together, and what breaks if you only override one? - Walk me through what happens inside a
HashMaponput(). ArrayListvsLinkedList: which do you actually use, and why?- What is type erasure, and what can’t you do because of it?
- What does
List.of()return, and what happens if you calladd()on it?
“Walk me through a HashMap put.” Here’s roughly how I’d say it:
“The map calls hashCode() on the key, spreads the bits a little, and uses that to pick a bucket in an array. If the bucket’s empty, the entry goes in. If not, it walks the entries in that bucket and uses equals() to check for the same key, replacing the value if it finds one. Too many collisions in one bucket and, since Java 8, the bucket can turn into a small balanced tree, once it passes eight entries and the table has at least 64 slots. When the map gets past its load factor, 0.75 by default, it resizes and redistributes everything. The practical upshot: a bad hashCode() that sends everything to one bucket turns your nice constant-time map into something much slower.”
It works because it ends on consequences. Have the mutable-key follow-up ready too: change a field that feeds hashCode() and the entry stays in its old bucket, so a lookup with the same object searches a different bucket and finds nothing. The entry is still there, taking memory, unreachable.
Most keys get their own bucket. A bad hashCode() piles them into one.
"ArrayList or LinkedList?" My honest take? Say ArrayList nearly every time, then explain yourself, because the explanation is what they’re grading. The elements sit next to each other in memory, which the CPU cache loves. The famous LinkedList win, cheap inserts in the middle, only shows up when you’re already holding a pointer to that spot, and how often does your code do that? Almost never. And if what you really wanted was a queue, reach for ArrayDeque.
Generics questions usually start with type erasure (at runtime List<String> and List<Integer> are the same class, which is why new T() won’t compile) and drift into wildcards, where the old Effective Java line still works: producer extends, consumer super.
Modern Java: 17, 21 and 25
Quick reality check before the questions. Oracle’s roadmap names 17, 21 and 25 as the long-term support releases (JDK 27 came out in September 2026, but it’s a short-lived one), and the service you’d be joining is probably on 17 or 21, maybe even 11 if it’s old enough. So ask the recruiter. It’s a normal question and it tells you what’s fair game.
The questions:
- What are records for, and what do you get for free?
- Show me sealed classes and pattern matching in a
switch. - What are virtual threads, and when would you not use them?
Records showed up in 16 and sealed classes in 17. Then 21 was the big one, with pattern matching for switch, record patterns, sequenced collections and virtual threads all going final. If you can write something like this from memory, and say why it doesn’t need a default branch, you’re fine:
sealed interface PaymentResult permits Approved, Declined, Pending {}
record Approved(String authCode) implements PaymentResult {}
record Declined(String reason) implements PaymentResult {}
record Pending(Duration retryAfter) implements PaymentResult {}
String describe(PaymentResult r) {
return switch (r) {
case Approved a -> "approved " + a.authCode();
case Declined(String reason) -> "declined: " + reason;
case Pending p -> "retry in " + p.retryAfter().toSeconds() + "s";
};
}
The compiler knows every permitted subtype, so the switch is exhaustive. Add a fourth result type later and every switch that forgot it stops compiling. That’s a better answer than “sealed classes restrict inheritance,” which is true and boring.
Don’t over-prepare preview features. Interviewers want to know whether you’ve used the stable ones and have an opinion.
Concurrency questions
Here’s where experienced candidates pull apart. Everybody has read about synchronized; far fewer can explain the day their thread pool ran dry.
- What does
volatileguarantee, and what doesn’t it? - Why is
count++not thread-safe, and how would you fix it? ConcurrentHashMapvsCollections.synchronizedMap?- What thread pool does
CompletableFuture.supplyAsync()use if you don’t pass one? - How do virtual threads change the way you write a blocking service?
Take volatile first, and keep the answer tight: “It gives you visibility and ordering, so every thread sees the latest write. It doesn’t give you atomicity. count++ on a volatile field is still a read, an add and a write, and two threads can interleave those. For a counter I’d use AtomicInteger, or LongAdder if a lot of threads are hammering it.”
ConcurrentHashMap has two details that tend to land well. It won’t take null keys or values, which bites people moving code over from HashMap. And computeIfAbsent is atomic per key, but the Javadoc warns that other updates can block while your function runs. So no network calls in there, please.
Many threads, one lock: everyone queues for the bridge, and throughput is whatever the bridge allows.
CompletableFuture is a sneaky one. Call the async methods without passing an executor and your work lands on ForkJoinPool.commonPool(), which the rest of the JVM shares, parallel streams included. Park blocking I/O in there and you can starve all of it. Pass your own executor and the problem goes away.
Virtual threads come up constantly now. JEP 444, which made them final in Java 21, makes two points worth repeating in the room: they’re for scale, not speed (they help when lots of tasks spend most of their time waiting), and you don’t pool them. Need to protect a fragile downstream? Use a Semaphore. If you want to show you’ve kept up, mention pinning. In Java 21, a virtual thread that blocked inside a synchronized block stayed stuck to its carrier thread, and JEP 491 in Java 24 got rid of nearly all of that.
Deadlocks are mostly about knowing the tool. jcmd <pid> Thread.print (or good old jstack) prints a “Found one Java-level deadlock” section naming the threads and the locks they’re each waiting on, and the fix is usually a consistent lock order. There’s more on this in our guide to concurrency and multithreading interview questions.
JVM memory and garbage collection
Nobody expects a GC engineer here. Know where things live, recognize the symptom, say what you’d check first, and you’re most of the way there.
- What lives on the heap, on the stack, and in metaspace?
- What’s the difference between “Java heap space” and “Metaspace” out-of-memory errors?
- How would you investigate a memory leak in a running service?
Start with where things live, since every follow-up builds on it. Objects go on the heap. Each thread gets its own stack for locals and references. Class metadata sits in metaspace, out in native memory (it took over from PermGen in Java 8). That’s why the two out-of-memory errors point in such different directions. “Java heap space” means too many live objects. “Metaspace” usually means something keeps loading classes and never lets go, and a redeploy that leaks classloaders is the classic culprit.
On collectors, I wouldn’t recite a list. G1 has been the default on server-class machines since JDK 9, and if that’s what you run, say so and say why it’s fine for your service. ZGC is the low-pause option (generational-only since JDK 24). If your team switched to it, they’ll want the reason, something like “p99 on that API mattered more to us than raw throughput.” “ZGC is better” isn’t a reason.
Leak questions are where you can sound like you’ve actually done it. Here’s the order I’d walk through. Is it really a leak? Watch heap-after-GC for a few hours. If the floor keeps rising, yes; if it rises and falls with traffic, probably not. Then grab a heap dump, either on demand with jcmd <pid> GC.heap_dump /tmp/heap.hprof or automatically by starting the JVM with -XX:+HeapDumpOnOutOfMemoryError. Open it in something like Eclipse MAT and look at what’s holding the most memory. It’s rarely exotic: an unbounded static cache, ThreadLocal values left behind on pooled threads, listeners that get registered and never removed. For the operating-system side of memory questions, see operating systems and memory management interview questions.
Backend scenario questions
At senior level, this part often carries the most weight. There’s no single right answer. They want to watch you debug.
- p99 latency doubled after this morning’s deploy. Walk me through it.
- Requests time out waiting for a database connection. What’s happening?
- A JPA endpoint got slow as the data grew. What do you suspect?
- How would you make a payment endpoint safe to retry?
For the latency question, don’t jump to a cause. Narrate a path. Something like this:
A triage path you can talk through out loud. The point is the order, not the boxes.
The slow JPA endpoint is nearly always an N+1 query: one query for the list, then one per row for a lazy relationship. Confirm it with SQL logging, fix it with a fetch join or batch fetching. For the retry question, they’re hoping to hear “idempotency key.”
Then come the stories. Two examples, in different voices. Both fictional.
Sample 1: Senior engineer, payments team, “Tell me about a production incident you led.”
Situation: “We had a checkout service on Spring Boot. One evening, requests started failing with connection timeouts, but the database itself looked healthy.”
Task: “I was on call, so getting checkout back was mine, and then the follow-up.”
Action: “I took a thread dump first. Almost every request thread was waiting to get a connection from the pool. The pool had ten connections, the Hikari default, and they were all held by threads stuck on an HTTP call to our fraud-check service, which had slowed down. The call was inside the @Transactional method, so each one held a database connection for the whole wait. I shortened the fraud-check timeout through config to stop the bleeding. The next day I moved that call out of the transaction, added a circuit breaker around it, and added an alert on pool wait time.”
Result: “Checkout recovered within the hour, and the same slowdown in fraud-check a month later didn’t touch us. The lesson I took: never hold a scarce resource while waiting on someone else’s network.”
Sample 2: Mid-level engineer, logistics company, “Describe a hard bug you found.” (flowing speech)
“Our route-pricing service fell over about once a week with ‘Java heap space.’ Restarts fixed it, which is why it lived so long. I set it to write a heap dump on OOM, waited for the next crash, and opened the dump. Most of the heap was one static map, a cache of pricing results. The key was a small class someone wrote without equals() and hashCode(), so every request created a key that never matched an old one. The cache just grew. I replaced the key with a record, which gets both methods from its fields, and swapped the map for a cache with a size limit. Heap after GC went flat. Kind of embarrassing, honestly, because it’s the first thing you learn about maps.”
Notice the Action part is the longest in both. That’s where the score comes from. Our STAR method guide goes deeper on timing.
How to say these answers out loud
Experienced developers mostly lose points on delivery, not knowledge. They open with JVM history, or go quiet for twenty seconds and then dump everything at once.
A shape that works for almost every technical question: give the one-line answer, explain the mechanism, mention one place it bit you or could bite you, and say when you’d choose differently. “synchronized is simpler and I’d default to it. ReentrantLock when I need a timeout on acquiring the lock or a fair lock. The thing that bites people is forgetting unlock() in a finally block.” Twenty seconds, and the interviewer has three things to follow up on.
Don’t know? Say so, then reason out loud: “I haven’t run ZGC in production, but my understanding is it trades some throughput for very short pauses, so I’d try it on the latency-sensitive service first.” That beats a confident guess every time.
And practice talking, not reading. Record yourself answering five of these and you’ll hear the “um, basically” habits right away. Our guide on communicating your thought process in technical interviews has more on narrating while you code.
Practicing with OfferBull
Here’s my bias: most Java prep is reading. People reread the HashMap source, skim a list like this one, nod along, and then freeze the first time they have to say it to an actual person. The practice that pays off is talking.
That’s the gap OfferBull is built for. It’s an AI mock interview app for iOS and Android where the interviewer speaks the questions out loud and waits for you to answer the same way. Paste in the job description (or upload your resume) and the questions lean toward the stack that team actually uses, so a Spring and Kafka shop won’t get you a pile of trivia about applets. It also follows up. Mention the Hikari incident and a minute later it’s asking why you didn’t just make the pool bigger, which is exactly the kind of poke a real interviewer makes. If you stall halfway through an answer, it can show suggestions and a STAR outline while you practice. English or Chinese, your call.
One thing we’re strict about: it’s for the weeks before the interview. On the day, it’s just you.
FAQ
Which Java version should I prepare for? Whichever one the team runs, and it’s completely fine to just ask the recruiter. If you can’t get an answer, I’d get comfortable with 17 and 21 (records, sealed types, pattern matching, virtual threads) and skim the 25 release notes the night before so nothing catches you off guard.
How deep do they go on JVM internals? Less deep than the scary blog posts suggest. Explain heap vs stack vs metaspace, say roughly why a generational collector works, describe what you’d look at first when memory keeps climbing, and you’ve covered most backend roles. Flag-by-flag GC tuning mostly comes up for performance-focused jobs.
Do senior people still get a coding round? Mostly, yeah, which annoys a lot of people with ten years on their resume. The problems usually aren’t brutal. They’re watching how clean your code is and whether you test it without being asked.
Practice This Out Loud
Try this tonight. Grab one question from each section, put your phone on record, and give yourself two minutes per answer. Then tell the worst production incident you’ve handled, start to finish, no notes. It’ll feel clunky the first time (it does for everyone). Once it doesn’t, run the same stories in a voice mock interview that pushes back. → Practice Now