JavaBy Experience Levelintermediate
Updated:

Java Interview Questions for 4 Years Experience

6 min read

The Java questions a mid-level developer with four years gets — production debugging, concurrency in services, transactions, caching and real implementation trade-offs.

TL;DR – Quick Answer

At four years, Java interviews move from definitions to implementation and production judgment. Expect scenario questions: how you debugged a memory leak or a slow endpoint, how you sized a thread pool, how you managed transactions and caching, how you designed a REST API, and how you handled concurrency in a real service. Interviewers grade whether you have shipped and operated code, not just written it.

On This Page

What a 4-year Java interview is really testing

By four years, interviewers assume you can write correct Java — what they want to know is whether you have shipped and operated it. The questions shift from "what is X" to "you had this production problem; walk me through what you did." A memory leak, a slow endpoint, a race condition under load: these scenarios reveal whether you diagnose systematically and understand the runtime behavior of your code, not just its syntax.

Answer with method and specifics. Name the tool, the observation, the hypothesis you ruled out, and the confirmed root cause before the fix. A story with a wrong turn you corrected is more credible than a suspiciously clean one.

Q1. How did you debug a memory leak in a Java service?

Capture a heap dump (jmap or on OutOfMemoryError with -XX:+HeapDumpOnOutOfMemoryError), open it in a tool like Eclipse MAT, and find the dominator — the object retaining the most memory. Trace its GC roots to see what is holding the reference that should have been released.

Common culprits worth naming: an unbounded cache or static collection that only grows, listeners never deregistered, ThreadLocals not cleared in a thread pool, and classloader leaks on redeploy. The method — heap dump, dominator tree, GC roots — is what proves you have actually done this, not read about it.

Interview note: Follow-up: "how did you confirm it was a leak and not just load?" Memory that never returns after GC across a stable load, a steadily climbing old-gen, and a growing retained set in successive dumps.

Q2. How do you size a thread pool for a service?

By workload type. CPU-bound work wants roughly the number of cores; I/O-bound work wants more threads because each spends most of its time blocked. A starting formula for I/O-bound tasks is cores × (1 + waitTime/computeTime), then tune with real latency numbers.

int cores = Runtime.getRuntime().availableProcessors();
ExecutorService io = new ThreadPoolExecutor(
    cores * 2, cores * 4, 60, TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(1000),               // bounded: backpressure
    new ThreadPoolExecutor.CallerRunsPolicy());   // don't silently drop

The senior detail is the bounded queue and rejection policy: Executors.newFixedThreadPool uses an unbounded queue that hides overload until you run out of memory. A four-year candidate should reach for ThreadPoolExecutor directly to control backpressure.

Interview note: Follow-up: "why not just a huge pool?" Context switching and memory per thread; too many threads degrade throughput and can starve the machine.

Q3. Explain transaction management and propagation in your services.

A transaction groups operations so they commit or roll back together. In Spring, @Transactional demarcates the boundary; propagation controls what happens when a transactional method calls another — REQUIRED (default) joins the existing transaction, REQUIRES_NEW suspends it and starts a fresh one.

The trap that catches mid-level developers is self-invocation: calling a @Transactional method from within the same class bypasses the proxy, so the annotation does nothing. Keep transactions short, don't wrap remote calls in them, and know that a RuntimeException rolls back by default while a checked exception does not unless you configure it.

Interview note: Trap: "why didn't your @Transactional roll back?" Either a checked exception (no rollback by default), a self-invocation bypassing the proxy, or the exception was caught and swallowed inside the method.

Q4. How do you add caching without introducing stale-data bugs?

Decide the caching pattern (cache-aside is most common), set a TTL that matches how stale the data may be, and define invalidation on writes. The hard part is not the cache — it is keeping it consistent with the source of truth.

Name the failure modes: stale reads when you cache too long without invalidation, a thundering herd when many requests miss simultaneously and hit the database, and cache stampede on expiry. Cache-aside with a modest TTL plus explicit invalidation on the write path covers most services.

Interview note: Follow-up: "cache-aside vs write-through?" Cache-aside loads on miss and the app manages the cache; write-through writes to cache and store together, trading write latency for read consistency.

Q5. How do you design a clean REST API?

Model resources as nouns, use HTTP verbs for actions, return correct status codes, version the API, and keep responses consistent. Design for the consumer: predictable URLs, meaningful errors, pagination for collections, and idempotent PUT/DELETE.

At four years you should also handle the operational side — validation with clear 400s, a consistent error body, and idempotency for retryable operations. The interviewer is checking whether you have designed an API others consumed, not just annotated a controller.

Interview note: Trap: "POST vs PUT for updates?" PUT is idempotent (same request repeated = same state); POST is not. Use PUT for full replacement, PATCH for partial, POST for creation or non-idempotent actions.

Q6. How did you optimize a slow endpoint?

Measure first — an APM trace or timing logs to find where the time goes. The usual answers: an N+1 query (fix with a fetch join or batch), a missing database index, an unbounded result set (paginate), or synchronous calls that could be parallel or cached.

// N+1: one query per order for its items
orders.forEach(o -> o.setItems(itemRepo.findByOrderId(o.getId())));
// Fixed: one query with a join fetch, or a single batched IN query
List<Order> orders = orderRepo.findAllWithItems();

The discipline being tested is measure-then-fix. Guessing at optimizations without a profile is the anti-pattern; a candidate who says "I profiled it and the N+1 was 80% of the latency" demonstrates real experience.

Interview note: Follow-up: "how did you find the N+1?" SQL logs showing repeated near-identical queries, or an APM span breakdown showing many small DB calls per request.

Q7. How do you handle concurrency in a real service?

Prefer stateless components so there is no shared mutable state to protect. Where state is unavoidable, use the right tool: ConcurrentHashMap for shared maps, AtomicInteger/LongAdder for counters, and immutability wherever possible. Reach for explicit locks only when the concurrent utilities don't fit.

The mid-level signal is recognizing shared mutable state as the source of concurrency bugs and defaulting to java.util.concurrent over hand-rolled synchronization. Mentioning that a Spring singleton bean shared across request threads must be thread-safe shows you connect the theory to the framework you use.

Interview note: Trap: "is a Spring @Service bean thread-safe by default?" No — singletons are shared across request threads, so any mutable instance field is a race unless you make it thread-safe or keep the bean stateless.

Q8. What is your testing strategy for a service?

Unit tests for business logic in isolation (fast, mock collaborators), integration tests for the wiring — repository, controller, database via an embedded or containerized instance — and a few end-to-end tests for critical paths. Aim the effort where bugs are costly, not at a coverage number.

At four years you should articulate the test pyramid and why: many fast unit tests, fewer integration tests, minimal brittle end-to-end tests. Mentioning testing failure paths — timeouts, exceptions, rollback — not just the happy path, marks production maturity.

Interview note: Follow-up: "how do you test transactional rollback?" An integration test that forces the exception and asserts the database is unchanged, confirming the boundary actually rolls back.

How to prepare

Prepare three production stories you can tell in detail — a debugging session, a performance fix, and a concurrency or data-consistency issue — each with the method, the tools, a wrong turn, and the confirmed fix. Those narratives carry a four-year interview more than any definition. Then make sure your fundamentals of transactions, thread pools and caching are precise, because the follow-ups drill into the mechanism behind your stories.

Sharpen the concurrency side with the multithreading questions, and to see where the bar rises next, review the 6 years experience questions on architecture and performance ownership. If you want to firm up any underlying concept, the Java learning path covers the fundamentals your stories rest on.

Frequently Asked Questions

How are 4-year Java interviews different from fresher interviews?
Freshers are asked what things are; mid-level developers are asked how they used them and what went wrong. At four years you get production scenarios — a leaking service, a slow query, a race condition — and are expected to describe your diagnosis and fix concretely. Definitions still matter but they are the floor, not the answer; ownership of real problems is what is tested.
What production skills should a 4-year Java developer demonstrate?
Reading a thread dump and a heap dump, sizing thread pools for a workload, managing transaction boundaries and understanding propagation, designing clean REST APIs, adding caching without stale-data bugs, and writing meaningful tests. The through-line is operating code in production, not just building it, so prepare stories where you diagnosed and resolved a real incident.
Do I need system design at 4 years of experience?
Light system design, yes — designing a single service, its data model, its API, and its failure handling — but not large-scale distributed architecture, which is expected later. Focus on component-level decisions you can defend: why this caching strategy, why these transaction boundaries, why this concurrency approach. Depth on your own service beats shallow breadth across systems you never built.
How do I answer a 'how did you debug X' question well?
Narrate the method, not just the fix: what you observed, what tools you used (thread dump, heap dump, profiler, logs, APM), the hypotheses you ruled out, and how you confirmed the root cause before fixing. Interviewers care that you diagnose systematically rather than guess. A concrete story with a wrong turn you corrected is more convincing than a clean-sounding fairy tale.
What concurrency knowledge is expected at 4 years?
Beyond definitions: sizing an ExecutorService, choosing ConcurrentHashMap over synchronized maps, using AtomicInteger for counters, understanding why a shared mutable field needs volatile or a lock, and recognising a race condition in code review. You are not expected to design a lock-free algorithm, but you must reason about shared state in the services you build.

Want to Build Your Career in Java Full Stack with AI?

Join CodeBegun and train with working industry engineers — Check the Java Full Stack training details

Apply for Demo Class →
Siva Prasad Galaba
Founder, CodeBegun · Staff Engineer

Founder of CodeBegun. 15+ years building Java systems at companies like Crunchyroll. Teaches Java, Spring Boot and system design the way the industry actually works, and mentors students through projects, mock interviews and placement preparation.

Technically reviewed by CodeBegun Technical TeamLast reviewed 16 July 2026 LinkedIn
Chat with us