40 questions with answers for candidates with 10+ years of experience — 10 medium, 30 hard.
What interviewers expect: 10 years experience
At ten years, roles are architect, engineering manager or principal level. Discussions cover enterprise architecture, cloud strategy, platform standards, build versus buy decisions, hiring and mentoring, and aligning technology with business goals. Hands-on Java depth is still checked, but communication, strategic thinking and influence across teams decide the outcome. Expect deep questions on past large decisions.
This set is weighted towards Spring Boot and Microservices (10), JDBC, Hibernate and JPA (7), Java 8+ Features and Streams (6), Multithreading and Concurrency (5).
Enterprise and cloud architecture Technology strategy Stakeholder management Hiring and mentoring
Solved at this levelStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
Exception chaining wraps a low level exception in a higher level one while keeping the original as the cause, for example throw new OrderException("save failed", sqlEx). The full stack trace shows both, with Caused by lines. It lets each layer speak its own language without losing the root cause, which is essential when debugging production logs.
At startup Spring Boot reads auto-configuration classes listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. Each class uses conditions like @ConditionalOnClass, @ConditionalOnMissingBean and @ConditionalOnProperty, so it creates beans only when the library is on the classpath and you have not defined your own. Run with --debug to see the condition report showing what was applied and why.
03
A HashMap shared by many threads sometimes loses updates. How do you fix it?
HashMap is not thread safe, so concurrent put calls can overwrite each other. Replace it with ConcurrentHashMap and use atomic methods instead of get-then-put, for example counts.merge(key, 1, Integer::sum) or computeIfAbsent for lazy creation. Collections.synchronizedMap also works but locks the whole map, which hurts throughput under contention.
04
Write a JPQL query that returns a DTO projection instead of entities.
Projections load only needed columns and avoid dirty checking.
public record OrderSummary(Long id, String customer, long total) {}
@Query("""
select new com.shop.OrderSummary(o.id, c.name, o.total)
from Order o join o.customer c
where o.createdAt >= :from
""")
List<OrderSummary> summaries(@Param("from") LocalDate from);
Interface-based projections in Spring Data are another option.
05
What is optimistic versus pessimistic locking in JPA?
Optimistic locking adds a @Version column; on update Hibernate checks the version and throws OptimisticLockException if another transaction changed the row, so you retry. It suits low contention. Pessimistic locking uses SELECT FOR UPDATE through LockModeType.PESSIMISTIC_WRITE, blocking others until commit; it suits high contention on the same rows, like seat booking, but can reduce throughput.
06
After moving to virtual threads, throughput did not improve and some requests stalled. What could cause it?
Common causes are pinning, where a virtual thread blocks inside a synchronized block or native call and holds its carrier thread; this was largely fixed in Java 24 for synchronized, but native calls still pin. Another cause is a fixed downstream bottleneck such as a small JDBC pool, so more threads just wait. Detect pinning with JFR events, use ReentrantLock in older versions and size pools to real capacity.
07
What is the difference between reduce() and collect()?
reduce() combines elements into a single immutable value using an identity and an associative function, such as summing numbers. collect() is a mutable reduction that accumulates into a container like a List, Map or StringBuilder, which is far more efficient for building collections. Using reduce to build a list creates many intermediate copies, so use collect for containers.
08
Memory usage is high because millions of Integer objects are stored in an ArrayList. What would you do?
Each Integer is a separate object with header overhead and a reference, so a List of Integer uses several times more memory than the raw data. If size is known, use an int[] array. Otherwise use primitive collections from libraries such as Eclipse Collections or fastutil, which store ints directly. This also improves cache performance and reduces GC pressure.
Parallel streams use the common ForkJoinPool and help only for large, CPU-bound, stateless work on sources that split well, like arrays and ArrayList. Avoid them for small data, I/O or blocking calls, LinkedList or iterate() sources, ordered operations like limit and findFirst, and shared mutable state. In a web server they compete for the same common pool, so measure before using them.
10
Configure a Resilience4j circuit breaker on a client call in Spring Boot.
Read Uncommitted allows dirty reads. Read Committed prevents dirty reads and is the default in PostgreSQL and SQL Server. Repeatable Read also prevents non-repeatable reads and is the MySQL InnoDB default. Serializable prevents phantom reads too but reduces concurrency. Choose the lowest level that keeps data correct, and use explicit locking for specific hot rows.
13
How would you split a monolith into microservices?
Do not split everything at once. Identify bounded contexts with the business, such as orders, payments and catalogue, and pick a module with clear boundaries and high change rate first. Use the strangler fig pattern: route its traffic through a gateway to the new service, give it its own database and sync data through events. Add observability and CI/CD before scaling out further.
14
Why is connection pooling important and how do you size a pool?
Opening a database connection is slow, so a pool like HikariCP keeps ready connections for reuse. A bigger pool is not always better, because the database has limited CPU and disk. A common starting point is around cores times two plus effective spindles per database, then tune with load tests. Watch pool wait time and active connections metrics, and set connection and query timeouts.
15
Should a service layer throw checked or unchecked exceptions? Defend your choice.
I prefer unchecked domain exceptions in the service layer. Checked exceptions force every caller to catch or declare them, leak low level details through layers and do not work well with lambdas. Unchecked exceptions with clear names, handled centrally by a controller advice, keep code clean. Spring follows the same idea by translating SQLException into its unchecked DataAccessException family.
A lambda can use a local variable from the enclosing method only if that variable is final or effectively final, meaning it is never reassigned after initialisation. Lambdas capture the value, not the variable, so this avoids confusing behaviour and races. To accumulate results, use a stream collector or an AtomicInteger instead of changing a captured local.
17
A thread pool fills up and tasks get rejected during traffic spikes. How do you handle it?
First check why tasks are slow, since slow tasks fill pools. Then choose a policy: a bounded queue with CallerRunsPolicy applies backpressure by running the task in the submitting thread; otherwise return a fast 503 so clients retry. Tune pool and queue sizes from load tests. Never use an unbounded queue, because it hides the problem until memory runs out.
18
Write a Kafka producer and consumer in Spring Boot.
Spring Kafka hides most boilerplate.
@Service
class OrderEvents {
private final KafkaTemplate<String, OrderPlaced> kafka;
OrderEvents(KafkaTemplate<String, OrderPlaced> k) { kafka = k; }
void publish(OrderPlaced e) { kafka.send("orders", e.orderId(), e); }
}
@Component
class OrderListener {
@KafkaListener(topics = "orders", groupId = "inventory")
void on(OrderPlaced e) { inventory.reserve(e); }
}
Using orderId as key keeps events for one order in the same partition and order.
19
How does JWT based authentication work in a Spring Boot API?
After login, the auth server issues a signed JWT with claims like user id, roles and expiry. The client sends it in the Authorization: Bearer header. The API, configured as an OAuth2 resource server in Spring Security, verifies the signature with the public key, checks expiry and builds the security context, so no session is needed. Keep tokens short-lived and use refresh tokens for renewal.
20
You need to add a new payment method to a system that has a big if-else on payment type. How would you redesign it?
This breaks the Open/Closed principle. I would create a PaymentProcessor interface with a pay() method and one implementation per type, such as UpiProcessor and CardProcessor. A factory or a Spring map of beans keyed by type picks the right one. Adding a new method then means adding one new class without touching existing tested code, and each processor can be unit tested alone.
21
Production shows Connection is not available, request timed out after 30000ms from HikariCP. What do you check?
The pool is exhausted. Check for connection leaks, where code holds connections without closing, by setting leakDetectionThreshold. Look for long transactions doing remote calls inside @Transactional, slow queries holding connections and a pool size too small for the thread count. Fix leaks, move external calls outside transactions, optimise slow queries and then size the pool from load tests.
22
Tell me about a time you solved a concurrency bug.
Situation: our wallet service sometimes showed wrong balances under load. Task: find the race and fix it without slowing the API. Action: I reproduced it with a JMeter test, found a read-then-update pattern in two concurrent requests, and replaced it with an atomic SQL update plus optimistic locking with a version column. Result: zero balance mismatches across festival traffic and a reusable load test for future changes.
23
Group a list of orders by city and sum the amount per city.
Use groupingBy with a summing collector.
Map<String, Double> totals = orders.stream()
.collect(Collectors.groupingBy(
Order::city,
TreeMap::new,
Collectors.summingDouble(Order::amount)));
TreeMap keeps cities sorted. For money, store amounts as long paise or BigDecimal and use Collectors.reducing with BigDecimal::add.
24
Two users booking the last seat both succeed. How do you prevent double booking?
This is a race between read and write. Use a database guarantee: a unique constraint on seat and show, an atomic conditional update like UPDATE seats SET status = 'BOOKED' WHERE id = ? AND status = 'FREE' and check the affected row count, or pessimistic locking with SELECT FOR UPDATE. Optimistic locking with a version column also works, retrying or failing the loser.
25
Tell me about a microservice you designed end to end.
Situation: our monolith's notification code slowed every release. Task: extract it into its own service. Action: I designed a Spring Boot service consuming Kafka events, with its own database for templates and delivery logs, retries with backoff, idempotent consumers and Actuator metrics in Grafana. Result: it handles 20 lakh messages a day, deploys independently and notification incidents dropped sharply.
26
A downstream payment service is slow and your API threads pile up. How do you protect your service?
Set strict connect and read timeouts on the HTTP client. Add a Resilience4j circuit breaker so calls fail fast once the error rate crosses a threshold, a bulkhead to limit concurrent calls and retry with backoff only for safe idempotent requests. Return a clear fallback such as payment pending. Monitor breaker state through Actuator metrics and alerts.
27
Legacy code uses anonymous classes and loops everywhere. How would you modernise it safely?
First add tests around the behaviour. Then refactor in small steps: anonymous Comparators and Runnables become lambdas, simple loops that filter and transform become streams, and null checks around returns become Optional where useful. Keep complex loops with early exits as loops if a stream is less readable. Use IDE refactorings and review each change to avoid altering behaviour.
28
Users report the app is slow for the first few minutes after every deployment. Why might this happen?
This is usually JVM warm-up. Code runs in the interpreter until the JIT compiles hot paths, and caches, connection pools and class loading are also cold. Fixes include warm-up traffic before adding the instance to the load balancer, pre-initialising pools, class data sharing (AppCDS) to reduce start time, or newer options like CRaC or GraalVM native image when fast start is critical.
29
A stream over a list of orders is slow in a hot path. How do you analyse it?
Profile first with JFR or async-profiler to confirm the stream is the hotspot. Check for repeated work like calling sorted() inside a loop, boxing overhead with Stream of Integer instead of IntStream, or re-streaming the same list many times. Cache computed maps, use primitive streams and, if still slow, a plain loop can be clearer and faster for very hot code.
30
An order must update inventory and payment in different services. How do you keep data consistent?
Distributed two-phase commit is fragile, so use the saga pattern. Each service performs a local transaction and publishes an event; failures trigger compensating actions, such as releasing stock when payment fails. Use the transactional outbox pattern so the database change and event publish never diverge, and make consumers idempotent because Kafka may deliver a message more than once.
31
How would you write a simple thread safe rate limiter in Java?
A token bucket with synchronized refill is simple and correct.
class RateLimiter {
private final long capacity, refillPerSec;
private double tokens; private long last = System.nanoTime();
RateLimiter(long cap, long rps) { capacity = cap; refillPerSec = rps; tokens = cap; }
synchronized boolean tryAcquire() {
long now = System.nanoTime();
tokens = Math.min(capacity, tokens + (now - last) / 1e9 * refillPerSec);
last = now;
if (tokens >= 1) { tokens--; return true; }
return false;
}
}
Across servers, keep the bucket in Redis.
Situation: three freshers joined my Spring Boot team at once. Task: make them productive quickly. Action: I created a starter task list, paired with each on their first story, gave detailed but kind code reviews and held a weekly session on topics like transactions and testing. Result: all three shipped features independently within a month and one now mentors new joiners.
33
How do you take a thread dump and a heap dump of a running Java process?
Find the process id with jps or ps, then use jcmd, which is the recommended tool.
jps -l
jcmd <pid> Thread.print > threads.txt
jcmd <pid> GC.heap_dump /tmp/heap.hprof
Older tools jstack and jmap do the same. Take three thread dumps a few seconds apart to spot stuck threads, and analyse heap dumps in Eclipse MAT.
34
A deep inheritance hierarchy of six levels is hard to change. How would you refactor it?
I would identify which behaviours actually vary and pull them into small interfaces, then replace inheritance with composition where a class holds strategy objects. Shared helper code can move to separate components. I would refactor step by step behind existing tests, starting from the leaves, so behaviour stays the same. The result is flatter classes that are easier to test and extend.
35
Your Spring Boot service takes 40 seconds to start in Kubernetes and pods get killed. How do you fix it?
First set a startupProbe so Kubernetes waits for startup before liveness checks begin. Then reduce start time: remove unused starters, enable lazy initialisation where safe, avoid heavy work in @PostConstruct, give enough CPU at start and consider AppCDS. For very fast startup, a GraalVM native image built with Spring AOT can start in under a second, with some trade-offs.
36
What is the Liskov Substitution Principle? Give an example of breaking it.
LSP says any subclass object must be usable wherever the parent is expected without breaking behaviour. A classic violation is Square extends Rectangle: setting width also changes height, so code that sets width 5 and height 4 and expects area 20 fails. Another is a subclass throwing UnsupportedOperationException for a parent method. The fix is a better hierarchy or composition.
37
GC pauses of two seconds are causing request timeouts. How would you reduce them?
Turn on GC logging with -Xlog:gc* and confirm which collections cause the pauses. Reduce allocation rate by fixing heavy object creation in hot paths. Size the heap correctly and set -Xms equal to -Xmx to avoid resizing. Tune G1 with MaxGCPauseMillis or move to ZGC for sub-millisecond pauses on large heaps. Retest under load before rolling out.
Sealed classes and interfaces, final in Java 17, restrict which classes can extend them using the permits clause, for example sealed interface Shape permits Circle, Square. Each subclass must be final, sealed or non-sealed. This models a closed set of types, and with pattern matching for switch in Java 21 the compiler can check that every case is handled without a default branch.
39
You need to process a 5 GB file using streams without running out of memory. How?
Use Files.lines(path), which reads lazily line by line, inside try-with-resources. Apply filter and map, then aggregate with a collector that keeps small results such as counts or sums instead of collecting all lines into a list. If the output is large, write it incrementally to a BufferedWriter. Avoid sorted() and distinct() on huge data because they buffer everything.
40
Add optimistic locking to an entity and handle the conflict.
Add a version field; Hibernate checks it on update.
@Entity
class Account {
@Id Long id;
long balance;
@Version Long version;
}
@Retryable(retryFor = ObjectOptimisticLockingFailureException.class, maxAttempts = 3)
@Transactional
public void debit(Long id, long amt) {
Account a = repo.findById(id).orElseThrow();
a.balance -= amt;
}
The update includes WHERE version = ?, so a stale write fails and is retried.
Frequently asked questions
What is asked in Java interviews for 10+ years of experience?
At ten years, roles are architect, engineering manager or principal level. Discussions cover enterprise architecture, cloud strategy, platform standards, build versus buy decisions, hiring and mentoring, and aligning technology with business goals. Hands-on Java depth is still checked, but communication, strategic thinking and influence across teams decide the outcome. Expect deep questions on past large decisions.
How many questions should I practise before a Java interview?
Work through all 40 questions here, then the company-wise sets for the employers you are targeting. Quality matters more than count: be able to explain why each answer is right.
Which topics matter most for 10+ years of experience?
Focus first on Enterprise and cloud architecture, Technology strategy, Stakeholder management, Hiring and mentoring.