Top 25 Java Interview Questions for 3 Years Experience (Expert Answers)
When interviewing for a Java Developer role with 3+ years of experience, technical interviewers shift focus away from simple syntax questions. Instead, they probe deep into concurrency mechanics, memory optimization, Spring Boot transaction boundaries, database indexing, and distributed microservices resilience.

1. Advanced Java Concurrency & Multithreading
Q1: How does ConcurrentHashMap achieve thread safety without locking the entire map?
In Java 7, ConcurrentHashMap used Segment-Level Locking (ReentrantLocks across 16 segments). In Java 8 and later, the design was completely re-architected for superior throughput:
- Lock-Free Reads: Read operations (
get()) are entirely lock-free. Node value and next pointers are markedvolatile, ensuring instant cross-thread memory visibility without synchronization overhead. - CAS (Compare-And-Swap) for Empty Buckets: When inserting into an empty bucket index via
put(), the JVM uses hardware-level CAS instructions (sun.misc.Unsafe/VarHandle) to insert the head node without acquiring any lock. - Synchronized on Head Node Only: If a collision occurs on an existing bucket, synchronization is locked only on the first node (head) of that specific bucket bin. Other buckets remain completely available for concurrent writes.
Q2: What is the difference between volatile and synchronized keywords?
| Feature | volatile |
synchronized |
|---|---|---|
| Core Guarantee | Visibility & Happens-Before ordering | Visibility + Mutual Exclusion (Atomicity) |
| Memory Effect | Forces reads/writes directly to Main Memory, bypassing CPU L1/L2 caches | Acquires a monitor lock, flushes local cache upon lock release |
| Atomicity (Compound Operations) | No (e.g. count++ is NOT thread-safe with volatile alone) |
Yes (ensures critical sections execute atomically) |
| Performance Overhead | Very low (no context switching) | Higher (thread blocking & kernel context switching) |
2. Spring Boot & Microservices Engineering
Q3: How does Spring manage transactions with @Transactional, and why does self-invocation break it?
Spring implements declarative transaction management via Spring AOP dynamic proxies (JDK Dynamic Proxies for interfaces or CGLIB for class proxies). When a transactional method is called:
- The proxy intercepts the call, opens a database connection, and disables auto-commit (
connection.setAutoCommit(false)). - The target method executes within the active transaction context.
- If the method completes successfully, the proxy commits the transaction. If an unhandled
RuntimeExceptionoccurs, the proxy triggers a database rollback.
The Self-Invocation Trap: If Method A calls Method B within the same class instance (
this.methodB()), the call completely bypasses the Spring AOP proxy. The@Transactionalannotation on Method B will be completely ignored!
@Service
public class OrderService {
// Calling this directly bypasses proxy on processPayment()
public void createOrder(Order order) {
saveOrder(order);
this.processPayment(order); // @Transactional WILL NOT WORK HERE!
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processPayment(Order order) {
// Payment processing logic
}
}
Q4: What is the Circuit Breaker pattern and how does Resilience4j prevent cascading failures?
In a distributed microservice topology, if Service B experiences high latency or downtime, synchronous calls from Service A can quickly exhaust Service A’s Tomcat worker threads, causing the entire system to crash (cascading failure).
A Circuit Breaker monitors outbound call failure rates across three states:
- CLOSED: Normal operation. All requests pass through.
- OPEN: Failure threshold exceeded (e.g., >50% errors). Requests fail immediately without hitting the downstream service, routing straight to a fallback response.
- HALF-OPEN: After a configured wait duration, a limited probe batch of requests is allowed through to test if the downstream service has recovered.
3. JPA, Hibernate & Database Performance
Q5: What is the N+1 Select Problem in Hibernate and how do you resolve it?
The N+1 problem occurs when fetching a collection of parent entities results in 1 initial query to fetch parent records, followed by N separate queries to fetch associated child entities for each parent row.
3 Ways to Eliminate N+1 Queries:
- JPQL JOIN FETCH: Explicitly fetch associations in a single SQL JOIN.
@Query("SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employees") List<Department> findAllWithEmployees(); - Entity Graphs (
@EntityGraph): Declaratively define fetch attributes at runtime without rewriting custom query strings. - Batch Fetching (
@BatchSize(size = 25)): Instructs Hibernate to fetch lazy collections using an SQLIN (...)clause in batches of 25 rather than 1-by-1.
4. Java 8+ Streams & Functional Programming Internals
Q6: What is the difference between Intermediate and Terminal operations in Streams?
Java Streams are lazy evaluated. Intermediate operations (filter(), map(), flatMap()) do not execute any computation when declared; they simply construct an execution pipeline graph.
Execution only triggers when a Terminal Operation (collect(), forEach(), reduce(), count()) is invoked. This allows the JVM to perform loop fusion and short-circuiting optimizations (processing items one-by-one in a single pass rather than creating intermediate collections).
// Demonstrating Stream pipeline efficiency
List<String> topPerformers = employees.stream()
.filter(e -> e.getSalary() > 100_000) // Intermediate (Lazy)
.map(Employee::getName) // Intermediate (Lazy)
.limit(5) // Short-circuiting
.collect(Collectors.toList()); // Terminal (Triggers Execution)
🎯 3-Year Developer Interview Checklist
- Master
CompletableFutureand asynchronous pipeline handling. - Understand the difference between Optimistic Locking (
@Version) and Pessimistic Locking (PESSIMISTIC_WRITE) in database transactions. - Be ready to explain how your microservices handle distributed tracing using Micrometer / OpenTelemetry and log correlation via MDC (Mapped Diagnostic Context).

3 Comments