Java backend interviews cover core language semantics, collections, concurrency, Spring, and how you reason about the JVM. Below are the questions asked most often, each with a model answer. Middle: deeper understanding, optimization, and real-world situations.
1
How does HashMap work internally, and what changed in Java 8?
Answer
Entries live in an array of buckets indexed by a function of the key hash; collisions form a linked list in the same bucket. Java 8 added treeification: once a bucket holds more than eight entries it becomes a red-black tree, so worst-case lookup went from O(n) to O(log n). It also spreads the hash with h ^ (h >>> 16) so high bits influence the low-bit index.
2
What does volatile guarantee, and what does it not?
Answer
It guarantees visibility and ordering: a write is published to other threads immediately and reads are not cached in a register. It does not guarantee atomicity, so a volatile counter still loses increments, because ++ is read-modify-write and two threads can read the same value. Use volatile for a flag many threads read, and AtomicInteger or synchronized when the operation combines read and write.
3
Explain @Transactional propagation. When does REQUIRES_NEW matter?
Answer
Propagation decides what happens when a transactional method is called from inside another transaction. REQUIRED, the default, joins the existing one — so a rollback anywhere rolls back everything. REQUIRES_NEW suspends the outer transaction and starts its own, which is what you want for an audit log or a notification that must survive the caller failing. The classic bug is expecting REQUIRED to isolate a failure when it cannot.
4
You see the N+1 problem in a Hibernate-backed endpoint. How do you find and fix it?
Answer
You find it by turning on SQL logging and counting queries for one request — the signature is one select followed by hundreds of near-identical ones. It happens when you load parents and then touch a lazy collection on each. Fixes are JOIN FETCH in JPQL, an entity graph, @BatchSize to fetch in groups, or a projection that selects only the fields the response needs. Switching to EAGER is not a fix; it moves the problem.
5
What is the difference between Stream and a for loop, and when is a loop better?
Answer
A stream describes a pipeline of lazy operations executed by a terminal call, which keeps filtering and mapping readable. A loop is better in a hot path where the abstraction costs measurable time, when the logic has side effects, and when a break in the middle would express it more clearly than a takeWhile. Parallel streams are the real trap — they help only for large CPU-bound work and usually hurt otherwise.
6
A service is slow and CPU is low. Where do you look?
Answer
Low CPU with poor throughput points at waiting rather than computing: blocking IO, an exhausted connection pool, lock contention, or an external call without a timeout. Take a thread dump and see what threads are actually parked on. Connection pool starvation is the most common culprit, and it usually traces back to a transaction held open across a network call.
7
What is the difference between Spring Boot auto-configuration and a plain @Configuration class?
Answer
Auto-configuration is conditional configuration shipped by a starter: classes annotated with @ConditionalOnClass or @ConditionalOnMissingBean that apply only when a dependency is on the classpath and you have not defined the bean yourself. That last part is what makes it feel magical and predictable at once — your own bean always wins, which is how you override any default.
8
What is the difference between `wait()`, `sleep()` and `join()`?
Answer
sleep pauses the current thread and keeps any lock it holds — which is how you deadlock while looking innocent. wait releases the monitor and parks the thread until notify, and it can only be called inside a synchronized block for that reason. join waits for another thread to finish. wait and notify belong to Object rather than Thread because the lock belongs to the object.
9
What does `transient` do, and where does serialization bite?
Answer
transient excludes a field from serialization, so on deserialization it comes back as the type's default. You use it for anything derived, anything huge, and anything secret — a password or a live connection has no business in a byte stream. The wider trap is that serialization freezes your class shape into a format: change the fields and old data fails to read unless serialVersionUID and compatibility were planned, which is why most teams use JSON or protobuf instead.
🦎
Reading answers is not enough
In a real interview you speak under pressure. Cam asks these same questions, scores every answer, and shows exactly what to fix.
Practice a Middle Java Backend interview →Free · 3 interviews per month