0% found this document useful (0 votes)
1 views22 pages

Java Senior Interview Guide

This document is a comprehensive interview guide for Senior Java Backend Developers, covering essential topics such as Core Java, Spring Boot, Microservices, Performance, and Debugging. It includes detailed explanations of key concepts, algorithms, and best practices, along with coding questions and answers. The guide aims to prepare candidates for real interview scenarios by providing in-depth knowledge and practical examples.

Uploaded by

manuj
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views22 pages

Java Senior Interview Guide

This document is a comprehensive interview guide for Senior Java Backend Developers, covering essential topics such as Core Java, Spring Boot, Microservices, Performance, and Debugging. It includes detailed explanations of key concepts, algorithms, and best practices, along with coding questions and answers. The guide aims to prepare candidates for real interview scenarios by providing in-depth knowledge and practical examples.

Uploaded by

manuj
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

JAVA BACKEND

Senior Developer Interview Guide

8 Years Experience | TCS & Product Company Ready

Core Java • Spring Boot • Microservices • Kafka • Docker • PostgreSQL

Covers 70%+ of real interview questions with deep answers & code examples
Table of Contents
PART 1: CORE JAVA
• JVM Memory Model & Memory Areas
• Garbage Collection (All Algorithms)
• OutOfMemoryError -- Diagnosis & Fix
• HashMap & HashSet Internals
• ConcurrentHashMap vs SynchronizedMap
• ClassLoaders & Delegation Model
• Fail-Fast vs Fail-Safe Iterators
• ExecutorService & Thread Pools
• Virtual Threads (Java 21)
• Java Locks -- All Types
• Java 8 Stream API -- Deep Dive
• Java 17 & 21 New Features
• String Pool & Immutability
• equals() hashCode() Contract
• Exception Handling Best Practices
• Generics & Wildcards

PART 2: SPRING & SPRING BOOT


• Spring Bean Lifecycle & Scopes
• Spring Auto-Configuration Internals
• Spring Batch -- Complete Guide
• Spring Security -- JWT & OAuth2
• Spring Data JPA -- N+1 & Optimizations
• Design Patterns in Spring
• REST API Design -- Production Ready
• Idempotency Patterns
• Transactional -- Deep Dive
• @Async & CompletableFuture

PART 3: MICROSERVICES & ARCHITECTURE


• Saga Pattern
• Circuit Breaker Pattern
• API Gateway Pattern
• CQRS & Event Sourcing
• Service Discovery
• Distributed Tracing

PART 4: PERFORMANCE & DEBUGGING


• Why is the API Slow? -- Complete Diagnosis
• JVM Tuning & GC Flags
• Memory Leak Detection
• Thread Dump Analysis
• Database Query Optimization
• Hibernate N+1 Problem

PART 5: KAFKA, DOCKER & POSTGRESQL


• Kafka -- Deep Dive
• Kafka Exactly-Once & Consumer Groups
• Docker -- Containers vs VMs
• PostgreSQL -- Indexing & EXPLAIN
• PostgreSQL VACUUM & MVCC

PART 6: CODING QUESTIONS


• Array & String Problems
• Collections & Streams
• Concurrency Problems
PART 1 -- CORE JAVA

Q: Explain JVM Memory Areas in detail

JVM memory is divided into distinct runtime data areas:

Memory Area Thread-Safe What Lives Here Common Issues


?

Heap Shared All object instances, arrays. Split into Young Gen (Eden OutOfMemoryError: Java heap space,
+ S0/S1) and Old Gen. GC overhead limit

Stack (per thread) Per-thread Stack frames -- local vars, operand stack, method StackOverflowError (deep recursion)
references.

Metaspace (Java 8+) Shared Class metadata, method bytecode, static variables, OutOfMemoryError: Metaspace (class
interned Strings. loader leak)

Code Cache Shared JIT-compiled native code. Perf drop if full;


-XX:ReservedCodeCacheSize

Native / JNI Stack Per-thread C/C++ native method frames. Rarely seen

PC Register Per-thread Address of current bytecode instruction. N/A

✔ Tip: Young Gen = Eden + Survivor0 + Survivor1. New objects go to Eden. After surviving a Minor GC they move to
Survivor spaces, and after N survivals (default 15) they are promoted to Old Gen.
■ Watch Out: PermGen was removed in Java 8 and replaced by Metaspace which auto-grows in native memory.
Class loader leaks cause Metaspace OOM.

Q: Explain Garbage Collection algorithms and when to use each

Generational GC is the foundation: Minor GC collects Young Gen (fast, stop-the-world), Major/Full GC collects Old
Gen (slow, longer pause). Collectors differ in HOW they collect:

Collector Pause Type Java Version Best For Key JVM Flag

Serial GC Stop-the-world All Single CPU, small heap (<100MB) -XX:+UseSerialGC

Parallel GC Stop-the-world Java 8 default Batch jobs, throughput over latency -XX:+UseParallelGC
(multi-thread)

CMS Mostly concurrent Deprecated Low-latency server apps (legacy) -XX:+UseConcMarkSweepGC


Java 9+

G1 GC Mostly concurrent Default Java 9+ General purpose, balanced -XX:+UseG1GC


throughput+latency, heap 4GB-16GB

ZGC Concurrent (<1ms Production Java Very large heaps (TB), ultra-low latency -XX:+UseZGC
pauses!) 15+

Shenandoah Concurrent OpenJDK 12+ Low latency similar to ZGC -XX:+UseShenandoahGC

G1 GC internals: Heap is divided into equal-sized regions (1-32MB). Regions are dynamically assigned as Eden,
Survivor, or Old. G1 builds a 'remembered set' per region to track cross-region references, enabling it to collect the
most garbage-dense regions first (hence 'Garbage First').
✔ Tip: For low-latency microservices: use ZGC. For batch jobs: use Parallel GC. Default G1 is fine for most Spring
Boot apps.

Q: How do you diagnose and fix OutOfMemoryError?

OOM messages tell you WHICH area is full:


OOM Message Root Cause Fix

Java heap space Too many live objects; memory leak; heap too Increase -Xmx; fix leak; use heap dump analysis
small

GC overhead limit exceeded GC spending >98% time but reclaiming <2% Same as above; likely leak or heap too small

Metaspace Class loader leak; too many dynamic Find leaking ClassLoader; set -XX:MaxMetaspaceSize
proxies/Lambdas generated

Direct buffer memory NIO [Link]() not freed; Netty Set -XX:MaxDirectMemorySize; check Netty leak
leak detector

Unable to create native thread OS thread limit hit; stack frames too large Reduce -Xss; increase OS ulimit; reduce thread count

Step-by-step OOM diagnosis:


• Enable heap dump on OOM: -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/[Link]
• Open heap dump in Eclipse MAT or VisualVM
• Look at 'Dominator Tree' -- largest object graph consumers
• Look at 'Leak Suspects' report -- MAT identifies likely leaks
• Common culprits: static Collections growing unbounded, ThreadLocal not cleaned, event listeners not
deregistered, Spring proxies, Hibernate 1st-level cache in batch jobs

// Common memory leak patterns -- know these! // 1. Static collection leak private static final
Map<String, Object> cache = new HashMap<>(); // grows forever! // 2. ThreadLocal leak in thread
pools ThreadLocal<SomeObject> tl = new ThreadLocal<>(); [Link](new SomeObject()); // If you
never call [Link](), the object lives as long as the thread // Thread pool threads live
forever -> LEAK. Always: try { [Link](value); // ... use it } finally { [Link](); // ALWAYS
clean up } // 3. Inner class holding outer reference class Outer { class Inner { } // Inner
holds implicit reference to Outer instance! // Use static nested class if Outer ref not needed
static class SafeInner { } }

Q: How does HashMap work internally? What changed in Java 8?

HashMap uses an array of buckets (Node<K,V>[]). The bucket index = (n-1) & hash(key).

put() flow:
• 1. Compute hash: hashCode() is further spread with (h ^ (h >>> 16)) to reduce collisions
• 2. Find bucket index
• 3. If bucket empty: place new Node
• 4. If key exists (equals() match): replace value
• 5. Else: add to end of linked list
• 6. Java 8+: If bucket chain length >= 8 AND total size >= 64: convert to Red-Black Tree (O(log n) worst
case vs O(n) before)
• 7. If size > capacity * loadFactor (0.75): resize -- double capacity and rehash all entries

// Critical interview knowledge HashMap<String, Integer> map = new HashMap<>(16, 0.75f); //


initial cap, load factor // WHY load factor 0.75? Balance between space waste (low LF) and
collisions (high LF) // Resize at 16 * 0.75 = 12 entries. New capacity = 32. // Thread Safety:
HashMap is NOT thread-safe // ConcurrentHashMap: thread-safe, no null keys/values //
[Link]: wraps with global lock (worse performance) // Key point: keys must
correctly implement equals() and hashCode() // If hashCode() always returns same value -> all
keys in one bucket -> O(n) performance!

✔ Tip: Java 8 treeification threshold = 8 (list to tree). Untreeify threshold = 6 (tree back to list). This prevents DOS
attacks via hash flooding.

Q: ConcurrentHashMap vs [Link] vs Hashtable


Feature Hashtable synchronizedMap ConcurrentHashMap

Null keys/values No Depends on wrapped NO (both null forbidden)


map

Locking Method-level lock Wrapper-level (entire Segment/bucket-level lock (Java


map) lock 8: CAS + synchronized per bin)

Concurrent reads Blocked by writes Blocked by writes Fully concurrent reads

Iterators Fail-fast (legacy) Fail-fast Weakly consistent -- never


throws CME

Performance Poor Poor (single lock) Excellent (fine-grained locking)

Use case Never use Simple single-map safety Production concurrent code
(legacy)

// ConcurrentHashMap Java 8+ internals: // - Array of Node<K,V>[] // - CAS (Compare-And-Swap)


operations for first node in empty bucket // - synchronized block only on head node for
collision chains // - This means 16 threads can write to 16 different buckets simultaneously!
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); // Atomic operations -- use
these instead of check-then-act: [Link]("key", 1); // atomic
[Link]("key", k -> expensiveOp(k)); // atomic [Link]("key", 1, Integer::sum);
// atomic increment // WRONG (not atomic even with CHM): if (![Link]("k")) {
[Link]("k", val); } // race condition!

Q: Explain ClassLoader hierarchy and delegation model

Java uses Parent Delegation Model: before loading a class, a ClassLoader delegates to its parent. Only if the
parent cannot find it, does the child try.
// ClassLoader hierarchy: // Bootstrap CL (native, loads [Link] / [Link] module) // +-
Platform CL (formerly Extension CL -- loads javax.*, etc.) // +- Application CL (loads your
classpath) // +- Custom ClassLoaders (OSGi, app servers, plugin systems) // Why parent
delegation? // 1. Security: rogue [Link] can't be loaded // 2. No duplicate class
loading // 3. Consistent behaviour // ClassLoader leak (common in app servers -- Tomcat,
JBoss): // Custom ClassLoader loads classes -> classes hold reference to CL // If CL not GC'd
-> all loaded classes stay in Metaspace -> OOM Metaspace // Fix: ensure ClassLoader is
dereferenced when app is undeployed

✔ Tip: In OSGi or Spring Boot's executable JAR, custom class loaders are used. Tomcat has a ClassLoader per
deployed webapp to isolate apps.

Q: What are Virtual Threads? (Java 21 -- Project Loom)

Simple definition: Virtual threads are lightweight threads managed by the JVM (not the OS). They are cheap --
you can create millions of them. They map M virtual threads onto N OS (platform) threads automatically.

Aspect Platform Thread Virtual Thread

Maps to 1 OS thread (1:1) Many-to-many (M:N) with carrier OS


threads

Memory ~1MB stack (reserved) ~few KB (grows/shrinks dynamically)

Creation cost Expensive (OS call) Extremely cheap (JVM managed)

Max count Thousands (OS limit) Millions

Blocking I/O Blocks OS thread Unmounts from carrier thread


(thread-free blocking)

CPU-bound work Good No benefit -- same as platform thread


Aspect Platform Thread Virtual Thread

ThreadLocal Works normally Works but be careful -- millions of them


= memory pressure

// Creating Virtual Threads (Java 21) Thread vt = [Link]().start(() -> { // This


thread is virtual [Link]("Running in: " + [Link]()); }); // With
ExecutorService (preferred in production) try (ExecutorService executor =
[Link]()) { for (int i = 0; i < 100_000; i++) {
[Link](() -> { // Each task gets its own virtual thread callSlowDatabase(); //
blocking -- JVM unmounts, no OS thread wasted! }); } } // auto-closes // Spring Boot 3.2+ --
enable virtual threads for Tomcat: // [Link]=true (in
[Link]) // This makes Spring MVC handle each request on a virtual thread!

When to use Virtual Threads:


• High-concurrency I/O bound apps: REST APIs making DB/HTTP calls, thousands of concurrent requests
• Replace thread pool sizing tuning -- just use one virtual thread per task
• Server-side request handling (Tomcat, Jetty with Spring Boot 3.2+)

When NOT to use Virtual Threads:


• CPU-intensive tasks (computation, image processing) -- use platform threads or ForkJoinPool
• When using synchronized blocks that pin the virtual thread to its carrier thread
• When code uses ThreadLocal extensively to store large objects -- millions of VTs = memory issue

■ Watch Out: PINNING: If virtual thread enters a synchronized block or calls native code, it 'pins' to its carrier OS
thread. Use ReentrantLock instead of synchronized to avoid pinning in virtual thread code.

Counter questions you'll get:


• Q: What's the difference between Virtual Thread and async/reactive (WebFlux)?
• A: Reactive uses non-blocking APIs with callback chains (complex code). Virtual threads let you write blocking,
imperative code that is internally non-blocking. Simpler code, similar performance.
• Q: Does Virtual Thread work with JDBC?
• A: Java 21 JDBC drivers are being updated. Some still use synchronized internally causing pinning. Use
connection pooling (HikariCP) to manage this.

Q: Explain all Java Locking mechanisms and when to use each

// 1. synchronized -- simplest, intrinsic lock, reentrant synchronized(this) { /* critical


section */ } public synchronized void method() { /* locks on 'this' */ } // 2. ReentrantLock --
explicit lock, more features ReentrantLock lock = new ReentrantLock(true); // fair=true: FIFO
order [Link](); try { // critical section } finally { [Link](); // ALWAYS in finally! }
// tryLock -- non-blocking attempt if ([Link](100, [Link])) { try { /*
work */ } finally { [Link](); } } // 3. ReadWriteLock -- multiple readers OR one writer
ReadWriteLock rwLock = new ReentrantReadWriteLock(); [Link]().lock(); // multiple
threads can hold simultaneously [Link]().lock(); // exclusive // 4. StampedLock (Java
8) -- optimistic reads! StampedLock sl = new StampedLock(); long stamp =
[Link](); // no lock acquired! double x = this.x; // read value if
(![Link](stamp)) { // check no write occurred stamp = [Link](); // fallback to real
read lock try { x = this.x; } finally { [Link](stamp); } } // 5. volatile -- visibility
guarantee only (no atomicity) private volatile boolean running = true; // all threads see
latest value // 6. Atomic classes -- CAS operations (lock-free) AtomicInteger counter = new
AtomicInteger(0); [Link](); // atomic, no lock
[Link](expected, newVal); // CAS

Lock Type Use When Avoid When

synchronized Simple critical sections, no timeout needed High contention, need tryLock
Lock Type Use When Avoid When

ReentrantLock Need tryLock, timed lock, interruptible lock, Simple cases (overhead)
fairness

ReadWriteLock Read-heavy data structures (config, caches) Write-heavy (lock overhead)

StampedLock Very read-heavy, optimistic read suits Need reentrancy (not reentrant!)

volatile Single flag, status variable (write once, read Compound check-then-act
many)

AtomicXxx Counters, accumulators, single-variable state Multiple variable atomicity


machines

Q: Deep dive into Java Stream API -- internals, performance, parallel streams

Streams are lazy pipelines. Intermediate ops build a pipeline descriptor; terminal op triggers traversal in a single
pass (fused).
// -- INTERMEDIATE (lazy) ----------------------------------------- // filter, map, flatMap,
distinct, sorted, peek, limit, skip, mapToInt, etc. // -- TERMINAL (eager, triggers pipeline)
-------------------------- // collect, forEach, reduce, count, findFirst, findAny, anyMatch, //
allMatch, noneMatch, min, max, toArray, sum, average // -- Short-circuiting terminals (can stop
early): ----------------- // findFirst(), findAny(), anyMatch(), allMatch(), noneMatch(),
limit() // REAL EXAMPLE -- common patterns: List<Order> orders = getOrders(); // Group by
status and count Map<Status, Long> countByStatus = [Link]()
.collect([Link](Order::getStatus, [Link]())); // Sum with mapping
double totalRevenue = [Link]() .filter(o -> [Link]() == COMPLETED)
.mapToDouble(Order::getAmount) .sum(); // FlatMap -- flatten nested lists List<String> allItems
= [Link]() .flatMap(o -> [Link]().stream()) .collect([Link]()); // --
PARALLEL STREAMS -------------------------------------------- List<String> results =
[Link]() .filter(s -> expensiveCheck(s)) .collect([Link]()); // Uses
[Link]() by default (shared!) // Good for: CPU-intensive, large data, no
shared mutable state // BAD for: I/O operations, small lists, operations with side effects //
Custom ForkJoinPool for parallel stream: ForkJoinPool pool = new ForkJoinPool(4);
[Link](() -> [Link]().forEach(this::process)).get();

■ Watch Out: Parallel streams use the COMMON ForkJoinPool shared by all parallel streams in the JVM. A slow
parallel operation blocks other parallel work. For I/O-bound parallel ops, use CompletableFuture with a custom
Executor instead.

Q: What are the important Java 17 and Java 21 features?

// -- JAVA 14+ RECORDS ----------------------------------------------- record Point(int x, int


y) { } // auto-generates: constructor, equals, hashCode, toString, getters // Compact
constructor for validation: record Range(int min, int max) { Range { if (min > max) throw new
IllegalArgumentException("invalid range"); } } // -- JAVA 17 SEALED CLASSES
--------------------------------------------- sealed interface Shape permits Circle,
Rectangle, Triangle { } record Circle(double radius) implements Shape { } record
Rectangle(double w, double h) implements Shape { } // Sealed classes + pattern matching =
exhaustive switch (compiler checks!) // -- JAVA 21 PATTERN MATCHING FOR switch (FINAL)
------------------------ double area = switch (shape) { case Circle c -> [Link] * [Link]() *
[Link](); case Rectangle r -> r.w() * r.h(); case Triangle t -> 0.5 * [Link]() * [Link]();
// No default needed -- sealed class, compiler knows all cases! }; // -- JAVA 15 TEXT BLOCKS
------------------------------------------------ // Java 15+ feature: multi-line strings
without escape characters // Triple-quoted strings called Text Blocks // String json =
[triple-quote] {"name":"John","age":30} [triple-quote]; // Useful for SQL, JSON, HTML strings
in Java code // -- JAVA 16 PATTERN MATCHING instanceof ------------------------------- if (obj
instanceof String s && [Link]() > 5) { // s is already cast and available here
[Link]([Link]()); } // -- JAVA 21 SEQUENCED COLLECTIONS
------------------------------------- SequencedCollection<String> list = new ArrayList<>();
[Link]("a"); [Link]("z"); String first = [Link](); // O(1) for all
SequencedCollection types

Q: equals() and hashCode() contract -- why important?

// CONTRACT: // 1. If [Link](b) == true, then [Link]() == [Link]() // 2. If hashCode


differs, equals MUST return false // 3. equals() must be: reflexive, symmetric, transitive,
consistent, null-safe // VIOLATION EXAMPLE (breaks HashMap!): class BadKey { String name;
public boolean equals(Object o) { return [Link](((BadKey)o).name); } // Forgot to override
hashCode! Uses [Link]() (identity) // Two BadKey("same") have same equals but
different hashCode // [Link]() will never find the key after put()! } // CORRECT (Java 7+
style): @Override public boolean equals(Object o) { if (this == o) return true; if (!(o
instanceof Person p)) return false; return age == [Link] && [Link](name, [Link]); }
@Override public int hashCode() { return [Link](name, age); // consistent with equals }
// Records auto-generate correct equals/hashCode -- use them for value objects!
PART 2 -- SPRING & SPRING BOOT

Q: Explain the complete Spring Bean Lifecycle

// FULL LIFECYCLE ORDER: // 1. Bean definition scanning (@Component, @Bean, XML) // 2.


BeanDefinition registered in BeanFactory // 3. BeanFactoryPostProcessor runs (e.g.
PropertySourcesPlaceholderConfigurer -- resolves @Value) // 4. Bean instantiated (constructor)
// 5. Dependencies injected (setter/field @Autowired) // 6. *Aware interfaces: setBeanName(),
setBeanFactory(), setApplicationContext() // 7.
[Link]() // 8. @PostConstruct method runs // 9.
[Link]() // 10. Custom init-method (if specified) // 11.
[Link]() -- AOP proxies created HERE // 12. Bean is
READY and in context // ... // 13. @PreDestroy method (on shutdown) // 14.
[Link]() // 15. Custom destroy-method @Component public class MyService
implements InitializingBean, DisposableBean { @PostConstruct public void init() {
[Link]("Step 8 -- PostConstruct"); } @Override public void afterPropertiesSet() {
[Link]("Step 9 -- afterPropertiesSet"); } @PreDestroy public void cleanup() {
[Link]("Step 13 -- PreDestroy"); } @Override public void destroy() {
[Link]("Step 14 -- [Link]"); } }

Scope Instance per Destroy called? Web only?

singleton (default) Spring context Yes No

prototype Each injection/getBean() call No (you manage it) No

request HTTP request Yes Yes

session HTTP session Yes Yes

application ServletContext Yes Yes

Q: How does Spring Boot Auto-Configuration work internally?

// @SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan //


Auto-config loading mechanism (Spring Boot 2.7+): // File:
META-INF/spring/[Link] // Contains
list of @AutoConfiguration classes // Spring Boot 2.6 and below: // File:
META-INF/[Link] // [Link]=//
[Link],... // HOW CONDITIONS WORK: @AutoConfiguration
@ConditionalOnClass([Link]) // only if DataSource on classpath
@ConditionalOnMissingBean([Link]) // only if user hasn't defined one
@ConditionalOnProperty("[Link]") // only if property present public class
DataSourceAutoConfiguration { @Bean public DataSource dataSource() { /* create and configure
DataSource */ } } // TO DEBUG auto-configuration: // Add --debug flag or
[Link]=DEBUG // Check /actuator/conditions
endpoint (shows all evaluated conditions) // TO EXCLUDE: @SpringBootApplication(exclude =
{[Link]})

Q: Explain @Transactional in depth -- propagation, isolation, common pitfalls

// PROPAGATION (what happens when @Transactional method calls another): // REQUIRED (default)
-- join existing tx, or create new if none // REQUIRES_NEW -- always create new tx, suspend
current if exists // NESTED -- run in nested tx (savepoint), rollback to savepoint on failure
// SUPPORTS -- run in tx if exists, else non-transactional // NOT_SUPPORTED -- suspend current
tx, run non-transactionally // MANDATORY -- must have existing tx, else throw exception //
NEVER -- must NOT have tx, else throw exception @Transactional(propagation =
Propagation.REQUIRES_NEW) public void auditLog(String msg) { /* always its own tx, not rolled
back with parent */ } // ISOLATION LEVELS (prevents concurrent tx problems): //
READ_UNCOMMITTED -- can read dirty data (fastest, almost never use) // READ_COMMITTED -- can't
read dirty data; phantom reads possible (PostgreSQL default) // REPEATABLE_READ -- same query
returns same data in tx; phantoms possible // SERIALIZABLE -- full isolation; slowest
@Transactional(isolation = Isolation.REPEATABLE_READ) public void criticalOperation() { } // --
COMMON PITFALLS ----------------------------------------------------- // 1. Self-invocation --
@Transactional DOES NOT work when calling within same class! @Service public class OrderService
{ @Transactional public void place(Order o) { validate(o); // @Transactional on validate() is
IGNORED -- no proxy! } @Transactional public void validate(Order o) { } // tx annotations
ignored in self-calls } // FIX: inject self (hacky) or extract to separate bean // 2.
@Transactional on private methods -- does nothing (proxy can't intercept) // 3. Exception
handling -- only RuntimeException (unchecked) triggers rollback by default!
@Transactional(rollbackFor = [Link]) // also rollback on checked exceptions // 4.
Transaction not active across thread boundaries @Transactional public void process() {
[Link](() -> [Link](obj)); // NEW THREAD -- no tx! }

✔ Tip: Use @Transactional(readOnly = true) for read-only methods. Hibernate optimizes -- skips dirty check, sets
connection read-only hint. Helps performance.

Q: Spring Data JPA -- N+1 Problem: explain, detect, and fix

N+1 is the most common JPA performance killer. It occurs when you load N entities and then JPA executes N
additional queries to load a related association.
// N+1 PROBLEM EXAMPLE: // Entity: @Entity public class Order { @OneToMany(fetch =
[Link]) // default for collections private List<OrderItem> items; } // In service:
List<Order> orders = [Link](); // 1 query: SELECT * FROM orders for (Order o :
orders) { [Link]().size(); // N queries: SELECT * FROM order_items WHERE order_id=? } //
Result: 1 + N queries. For 1000 orders = 1001 DB roundtrips! // -- DETECTION
---------------------------------------------------------- // 1. Enable SQL logging:
[Link]-sql=true // 2. Use Hibernate statistics:
[Link].generate_statistics=true // 3. Look for many identical SELECTs
in logs with different ID params // 4. Use p6spy or datasource-proxy to log all queries with
stack traces // -- FIXES -------------------------------------------------------------- // FIX
1: JPQL JOIN FETCH (most common) @Query("SELECT o FROM Order o JOIN FETCH [Link] WHERE
[Link] = :status") List<Order> findByStatusWithItems(@Param("status") Status status); // FIX
2: Entity Graph (flexible, reusable) @EntityGraph(attributePaths = {"items", "[Link]"})
List<Order> findAll(); // FIX 3: Batch fetching (Hibernate feature -- transparent)
@OneToMany(fetch = [Link]) @BatchSize(size = 100) // loads items for 100 orders at once
instead of 1-by-1 // FIX 4: DTO projection (best performance -- only fetch needed columns)
@Query("SELECT new [Link]([Link], [Link], COUNT(i)) " + "FROM Order o LEFT JOIN
[Link] i GROUP BY [Link], [Link]") List<OrderDTO> findOrderSummaries(); // CAUTION: JOIN FETCH
with pagination is WRONG: @Query("SELECT o FROM Order o JOIN FETCH [Link]") // + Pageable //
Hibernate warns: HHH90003004 -- firstResult/maxResults with FETCH JOIN -- applies in memory! //
FIX: Use @BatchSize or two-query approach for paginated collections

Q: Explain Spring Security JWT authentication flow

// JWT FLOW: // 1. Client POSTs /login with credentials // 2. Server validates, generates JWT:
[Link] // 3. Client stores JWT (memory or httpOnly cookie) // 4. Client sends
JWT in Authorization: Bearer <token> header // 5. Server validates JWT signature and expiry on
each request // JWT STRUCTURE: // Header: {"alg":"HS256","typ":"JWT"} // Payload:
{"sub":"userId","roles":["ADMIN"],"exp":1234567890} // Signature: HMACSHA256(base64(header) +
"." + base64(payload), secret) // SPRING SECURITY FILTER CHAIN: @Component public class
JwtAuthFilter extends OncePerRequestFilter { @Override protected void
doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws
ServletException, IOException { String header = [Link]("Authorization"); if (header !=
null && [Link]("Bearer ")) { String token = [Link](7); if
([Link](token)) { String username = [Link](token); UserDetails user
= [Link](username); UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, null, [Link]());
[Link](new WebAuthenticationDetailsSource().buildDetails(req));
[Link]().setAuthentication(auth); } } [Link](req, res); } }
// COMMON PITFALLS: // 1. JWT stored in localStorage -- vulnerable to XSS. Use httpOnly
cookies! // 2. No token refresh mechanism -- user logs out when token expires // 3. Not
validating JWT expiry on every request // 4. Weak secret -- use RS256 (asymmetric) for
distributed systems

Q: @Async and CompletableFuture -- when and how?

// @Async -- runs method in separate thread pool @Configuration @EnableAsync public class
AsyncConfig { @Bean public Executor asyncExecutor() { ThreadPoolTaskExecutor exec = new
ThreadPoolTaskExecutor(); [Link](5); [Link](10);
[Link](100); [Link]("async-");
[Link](new CallerRunsPolicy()); // fallback [Link]();
return exec; } } @Service public class EmailService { @Async public CompletableFuture<Boolean>
sendEmail(String to, String body) { // runs in asyncExecutor thread pool boolean sent =
[Link](to, body); return [Link](sent); } } //
CompletableFuture -- compose async operations: CompletableFuture<User> userFuture =
[Link](userId); CompletableFuture<Account> accountFuture =
[Link](userId); // Run both concurrently, combine results:
[Link](userFuture, accountFuture) .thenApply(v -> new
Dashboard([Link](), [Link]())) .exceptionally(ex -> { [Link]("Failed",
ex); return [Link](); }); // Chain operations: [Link](() ->
fetchUser(id)) .thenApply(user -> enrichUser(user)) .thenAcceptAsync(user -> notify(user),
notificationExecutor) .exceptionally(ex -> { handleError(ex); return null; });
PART 3 -- MICROSERVICES & ARCHITECTURE

Q: Saga Pattern -- Choreography vs Orchestration (with real example)

A Saga is a sequence of local transactions, each publishing events/messages that trigger next step. On failure,
compensating transactions undo previous steps.
// E-COMMERCE ORDER SAGA EXAMPLE: // Steps: CreateOrder -> ReserveInventory -> ChargePayment ->
ConfirmOrder // Compensations: CancelOrder <- ReleaseInventory <- RefundPayment // --
CHOREOGRAPHY (event-driven, no central coordinator) --------------- // Each service listens to
events and reacts @KafkaListener(topics = "order-created") public void
onOrderCreated(OrderCreatedEvent e) { try { [Link]([Link](), [Link]());
[Link](new InventoryReservedEvent([Link]())); } catch
(InsufficientStockException ex) { [Link](new
InventoryReservationFailedEvent([Link]())); // Order service listens to this -> cancels
order } } // PRO: Simple, loose coupling, no SPOF // CON: Hard to track overall saga state,
complex error flows, risk of cyclic events // -- ORCHESTRATION (central coordinator)
------------------------------- @Service public class OrderSagaOrchestrator { public void
execute(CreateOrderCommand cmd) { SagaContext ctx = new SagaContext([Link]()); try {
[Link](cmd); // Step 1 [Link](cmd); // Step 2
[Link](cmd); // Step 3 [Link]([Link]()); // Step 4 } catch
(PaymentFailedException e) { [Link](cmd); // compensate step 2
[Link]([Link]()); // compensate step 1 } } } // PRO: Easy to understand
flow, easy to add steps, central monitoring // CON: Orchestrator can become a bottleneck,
coupling // PRODUCTION TOOLS: [Link], Camunda, AWS Step Functions, Netflix Conductor

Q: Circuit Breaker -- States, configuration, and Resilience4j example

// STATES: // CLOSED (normal) -> failures counted -> threshold exceeded -> OPEN // OPEN (fail
fast) -> wait duration -> HALF_OPEN // HALF_OPEN (probe) -> test calls -> success -> CLOSED,
failure -> OPEN // Resilience4j (Spring Boot): // pom: resilience4j-spring-boot3
@CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback") @Retry(name =
"paymentService", fallbackMethod = "paymentFallback") @TimeLimiter(name = "paymentService")
public CompletableFuture<PaymentResult> chargePayment(PaymentRequest req) { return
[Link](() -> [Link](req)); } public
CompletableFuture<PaymentResult> paymentFallback(PaymentRequest req, Exception e) {
[Link]("Payment service failed, using fallback", e); return
[Link]([Link]()); } // [Link] config: /*
resilience4j: circuitbreaker: instances: paymentService: slidingWindowType: COUNT_BASED # or
TIME_BASED slidingWindowSize: 10 # last 10 calls failureRateThreshold: 50 # open if 50% fail
waitDurationInOpenState: 30s # wait 30s before half-open
permittedNumberOfCallsInHalfOpenState: 3 slowCallRateThreshold: 50 # also trips on slow calls
slowCallDurationThreshold: 3s retry: instances: paymentService: maxAttempts: 3 waitDuration:
500ms exponentialBackoffMultiplier: 2 # 500ms, 1s, 2s */

Q: CQRS and Event Sourcing -- what, why, when

CQRS (Command Query Responsibility Segregation): Separate the read model (query) from the write model
(command). Commands change state; queries return data. They can use different databases.
Event Sourcing: Instead of storing current state, store the sequence of events that led to it. State is rebuilt by
replaying events.
// CQRS Example: // Write side: handles commands @CommandHandler public void
handle(PlaceOrderCommand cmd) { Order order = new Order([Link](), [Link]());
[Link](); [Link](order); // Write DB (normalized, ACID)
[Link](new OrderPlacedEvent(order)); } // Read side: handles queries (denormalized
for fast reads) @EventHandler public void on(OrderPlacedEvent event) { // Update read-optimized
projection OrderView view = new OrderView([Link](), [Link](), ...);
[Link](view); // Read DB (denormalized, Elasticsearch/Redis) } @QueryHandler
public OrderSummaryDTO handle(GetOrderSummaryQuery query) { return
[Link]([Link]()); // Fast! } // EVENT SOURCING: // Instead of:
UPDATE orders SET status='PAID' WHERE id=? // Store: {type: "OrderPaid", orderId: "123",
amount: 99.99, timestamp: ...} // Current state = replay all events from event store //
BENEFITS: Full audit log, time travel (replay to any point), event-driven // DRAWBACKS:
Eventual consistency, complex queries, event schema evolution
PART 4 -- PERFORMANCE & DEBUGGING

Q: WHY IS THE API SLOW? -- Complete systematic diagnosis (most important answer!)

This is the most common senior interview question. Walk through these areas systematically:

1. APPLICATION LAYER
• Thread pool exhaustion: All Tomcat threads busy. Fix: Increase [Link] (default 200), or
switch to Virtual Threads (Spring Boot 3.2), or use reactive (WebFlux)
• Inefficient code: O(n²) algorithm in request path, unnecessary loops, repeated computation. Use profiler to find
hotspots.
• Blocking calls on main thread: Synchronous HTTP calls to other services. Use async/parallel execution with
CompletableFuture.
• Missing caching: Expensive computation or DB call repeated on every request. Use @Cacheable with Redis.
• Large object serialization: Jackson serializing huge objects. Use DTOs with only needed fields.

2. DATABASE LAYER (most common cause!)


• N+1 Problem: JPA LAZY loading triggering individual queries per entity. Fix: JOIN FETCH, @EntityGraph,
@BatchSize.
• Missing index: Full table scan instead of index scan. Detect: EXPLAIN ANALYZE in PostgreSQL. Fix: add
index on filtered/joined columns.
• Connection pool exhaustion: HikariCP pool full, requests queue. Default pool = 10 connections. Fix: tune
pool size, check for connection leaks (transaction not committed/rolled back).
• Slow queries: Complex joins, no pagination, Cartesian product from wrong JOIN. Use query timeout,
pagination, EXPLAIN ANALYZE.
• Lock contention: Row-level or table-level locks causing waits. Check pg_locks, pg_stat_activity.
• Hibernate dirty checking: Hibernate checks ALL fields of ALL loaded entities at tx commit. Fix: use DTOs for
read-only ops, use @Transactional(readOnly=true).

3. JVM / MEMORY LAYER


• GC pauses: Stop-the-world GC pausing threads. Detect: enable GC logging (-Xlog:gc*). Fix: tune heap size,
switch to ZGC for low-latency.
• Memory leak: Heap grows over time, GC keeps running but can't free. Detect: heap dump + MAT analysis. Fix:
find unbounded collections, ThreadLocal leaks.
• JIT compilation: Slow at startup (code not yet JIT-compiled). Detect: compare p99 latency at startup vs steady
state. Fix: JVM warm-up, GraalVM native image.

4. EXTERNAL DEPENDENCIES
• Downstream service slow: HTTP call to payment/inventory service timing out. Detect: distributed tracing
(Jaeger/Zipkin). Fix: timeout + circuit breaker + fallback.
• Redis slow: Large keys, hot keys, network latency. Detect: Redis SLOWLOG, latency monitor. Fix: pipeline
commands, reduce key size.
• Kafka consumer lag: Consumer processing slower than producer. Detect: [Link]
--describe. Fix: increase partitions + consumers.

// DEBUGGING TOOLKIT -- commands to run: // 1. Thread dump (see what all threads are doing):
jstack <pid> > thread_dump.txt // Look for: BLOCKED threads, threads waiting on locks, thread
pool states // 2. Heap dump: jmap -dump:format=b,file=[Link] <pid> // Analyze with Eclipse
MAT // 3. GC log analysis: java -Xlog:gc*:file=[Link]:time,level,tags -jar [Link] // or use
GCViewer / [Link] for visualization // 4. CPU profiling (Java Flight Recorder -- zero
overhead!): java -XX:StartFlightRecording=duration=60s,filename=[Link] -jar [Link] //
Analyze with JDK Mission Control // 5. PostgreSQL slow query analysis: EXPLAIN (ANALYZE,
BUFFERS, FORMAT TEXT) SELECT ...; -- Look for: Seq Scan (no index), high Rows Removed, large
Buffers hit -- Enable pg_stat_statements for automatic slow query tracking // 6. Spring Boot
Actuator metrics: // GET /actuator/metrics/[Link] -- response times // GET
/actuator/metrics/[Link] -- DB pool status // GET /actuator/metrics/[Link]
-- GC pause times

Q: How to detect and fix Memory Leaks in production Java?

// DETECTION STEPS: // 1. Monitor heap over time -- if it grows without bound and GC can't
reclaim -> leak // 2. Enable GC logging and watch: if OldGen keeps growing after Full GC ->
leak // 3. Take heap dump: -XX:+HeapDumpOnOutOfMemoryError OR jmap -dump:... // COMMON MEMORY
LEAK PATTERNS AND FIXES: // PATTERN 1: Static collections growing forever private static
Map<String, Session> activeSessions = new HashMap<>(); // FIX: Use Guava Cache with expiry, or
ConcurrentHashMap + manual cleanup private static Cache<String, Session> sessions =
[Link]() .expireAfterWrite(30, MINUTES) .maximumSize(10_000) .build(); // PATTERN
2: Event listeners not deregistered [Link](this); // on startup // NEVER calls
[Link](this); -> this object never GC'd! @PreDestroy public void cleanup() {
[Link](this); } // PATTERN 3: ThreadLocal in thread pool ThreadLocal<Connection>
localConn = new ThreadLocal<>(); // Pool thread is reused -> old Connection from previous
request still there! try { [Link](getConnection()); process(); } finally {
[Link](); // MANDATORY in thread pool context } // PATTERN 4: Hibernate 1st-level
cache in batch processing @Transactional public void processBatch(List<Long> ids) { for (Long
id : ids) { // 1 million IDs Entity e = [Link](id).get(); // all loaded into 1st-level
cache! process(e); // Heap fills with all 1M entities! } } // FIX: for (Long id : ids) { Entity
e = [Link](id).get(); process(e); [Link](e); // or [Link]()
periodically }
PART 5 -- KAFKA, DOCKER & POSTGRESQL

Q: Kafka deep dive -- Partitions, Consumer Groups, Exactly-Once, Ordering

Core architecture: Topics are divided into Partitions. Each partition is an ordered, immutable log on disk.
Partitions are replicated across brokers (replication factor). One broker is leader per partition -- handles
reads/writes.
// KEY CONCEPTS: // PARTITION ORDERING: // Messages within a partition are strictly ordered. //
Across partitions: NO ordering guarantee. // Use partition key to keep related messages in same
partition: ProducerRecord<String, Order> record = new ProducerRecord<>("orders",
[Link](), order); // key = customerId // All orders for same customer -> same
partition -> processed in order // CONSUMER GROUPS: // Each consumer group independently reads
all messages from a topic. // Within a group: each partition is assigned to exactly one
consumer. // Parallelism = number of partitions. More partitions = more parallel consumers. //
If consumers > partitions, extra consumers are idle. // DELIVERY SEMANTICS: // At-most-once:
auto-commit offsets BEFORE processing (message lost if consumer crashes) // At-least-once:
commit AFTER processing (may reprocess on crash) -- most common // Exactly-once: Kafka
transactions (producer + consumer in same transaction) // SPRING KAFKA AT-LEAST-ONCE (default):
@KafkaListener(topics = "orders", groupId = "order-processor") public void consume(Order order)
{ processOrder(order); // if this fails, offset not committed, message redelivered } //
EXACTLY-ONCE with transactions: @Transactional // Spring manages Kafka + DB transaction
together @KafkaListener(topics = "orders") public void consume(Order order, Acknowledgment ack)
{ [Link](toEntity(order)); // DB write [Link](); // commit Kafka offset -- all
or nothing } // CONSUMER LAG: // lag = latest_offset - committed_offset // High lag = consumer
can't keep up with producer // Monitor: [Link] --bootstrap-server :9092
--describe --group mygroup // Fix: increase partitions, add more consumer instances (up to
partition count) // PRODUCER RELIABILITY: Properties props = new Properties();
[Link]("acks", "all"); // wait for all ISR replicas to ack [Link]("retries", 3);
[Link]("[Link]", "true"); // exactly-once producer (deduplicates retries)

Q: PostgreSQL -- Indexing, EXPLAIN ANALYZE, and VACUUM

// INDEX TYPES: // B-tree (default) -- equality, range queries: =, <, >, BETWEEN, LIKE
'prefix%' // Hash -- only equality: = (rarely needed, B-tree usually better) // GIN --
full-text search, JSONB @>, array operators // GiST -- geometric types, full-text, range
overlap // BRIN -- very large tables where physical order matches query order (time-series!)
CREATE INDEX idx_orders_status ON orders(status); -- B-tree CREATE INDEX idx_orders_created ON
orders(created_at DESC); -- for ORDER BY CREATE INDEX idx_orders_composite ON
orders(customer_id, status); -- composite CREATE INDEX idx_orders_covering ON
orders(customer_id) INCLUDE (status, total); -- covering CREATE INDEX idx_orders_partial ON
orders(status) WHERE status = 'PENDING'; -- partial -- COMPOSITE INDEX ORDER MATTERS! --
(customer_id, status) helps: WHERE customer_id=? AND status=? -- or: WHERE customer_id=? --
Does NOT help: WHERE status=? (can't skip first column) -- EXPLAIN ANALYZE output
interpretation: EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id = 42 AND
status = 'PENDING'; -- "Seq Scan" -> no index used -> add index! -- "Index Scan" -> good --
"Index Only Scan" -> best (covering index, no heap access) -- "Bitmap Heap Scan" -> medium --
"Rows Removed by Filter: 9999" -> index not selective enough -- VACUUM: -- PostgreSQL MVCC:
UPDATE doesn't overwrite, creates new row version (dead tuples) -- VACUUM removes dead tuples,
reclaims space -- autovacuum runs automatically -- but may need tuning for high-write tables --
VACUUM ANALYZE also updates planner statistics (run after bulk inserts!) -- LOCKS: SELECT pid,
query, state, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event IS NOT NULL;
-- see waiting queries SELECT * FROM pg_locks WHERE NOT granted; -- see blocked locks

Q: Docker -- key concepts and production tips


# OPTIMAL DOCKERFILE (multi-stage, layer caching): FROM eclipse-temurin:21-jdk AS builder
WORKDIR /app COPY [Link] . COPY src src RUN mvn package -DskipTests FROM
eclipse-temurin:21-jre # smaller JRE-only base image WORKDIR /app COPY --from=builder
/app/target/*.jar [Link] # Run as non-root (security best practice): RUN useradd -r -u 1001
appuser && chown appuser /app USER appuser EXPOSE 8080 # Use exec form (not shell form) so
SIGTERM is properly received: ENTRYPOINT ["java", "-jar", "[Link]"] # LAYER CACHING TIP: #
Copy [Link] first, run mvn dependency:go-offline, THEN copy src # Dependencies layer is cached
unless [Link] changes # HEALTH CHECK: HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD
curl -f [Link] || exit 1 # JVM IN CONTAINERS: # Java 8u191+ /
Java 10+ is container-aware: reads cgroup limits # Set: -XX:MaxRAMPercentage=75.0 (use 75% of
container memory) # Avoid hardcoded: -Xmx512m (ignores container memory limit) # For Docker:
docker run -m 512m -e JAVA_OPTS="-XX:MaxRAMPercentage=75.0" app
PART 6 -- CODING QUESTIONS

Q: Write a thread-safe Singleton in Java

// BEST: Enum singleton (thread-safe, serialization-safe, reflection-safe) public enum


AppConfig { INSTANCE; private final String dbUrl = [Link]("DB_URL"); public String
getDbUrl() { return dbUrl; } } // GOOD: Double-checked locking with volatile public class
DatabasePool { private static volatile DatabasePool instance; // volatile prevents instruction
reordering! private DatabasePool() { } public static DatabasePool getInstance() { if (instance
== null) { // first check (no lock) synchronized ([Link]) { if (instance == null) {
// second check (with lock) instance = new DatabasePool(); } } } return instance; } } //
ELEGANT: Bill Pugh (Initialization-on-demand holder) public class MetricsRegistry { private
MetricsRegistry() { } private static class Holder { // Inner class not loaded until
getInstance() is called // JVM guarantees class initialization is atomic static final
MetricsRegistry INSTANCE = new MetricsRegistry(); } public static MetricsRegistry getInstance()
{ return [Link]; } }

Q: Write a Producer-Consumer using BlockingQueue

public class ProducerConsumer { private static final BlockingQueue<String> queue = new


LinkedBlockingQueue<>(100); static class Producer implements Runnable { @Override public void
run() { for (int i = 0; i < 50; i++) { try { String item = "Item-" + i; [Link](item); //
blocks if queue is full [Link]("Produced: " + item); } catch (InterruptedException
e) { [Link]().interrupt(); } } } } static class Consumer implements Runnable {
@Override public void run() { while (true) { try { String item = [Link](1,
[Link]); // blocks up to 1s if (item == null) break; // no more items
[Link]("Consumed: " + item); } catch (InterruptedException e) {
[Link]().interrupt(); break; } } } } public static void main(String[] args)
throws InterruptedException { ExecutorService exec = [Link](3);
[Link](new Producer()); [Link](new Consumer()); [Link](new Consumer()); // 2
consumers, 1 producer [Link](); [Link](10, [Link]); } }

Q: Stream API -- Group employees by department, find top 3 earners per dept

record Employee(String name, String department, double salary) {} List<Employee> employees =


[Link]( new Employee("Alice", "Engineering", 120000), new Employee("Bob", "Engineering",
95000), new Employee("Carol", "Marketing", 85000), new Employee("Dave", "Engineering",
110000), new Employee("Eve", "Marketing", 90000) ); // Top 3 earners per department:
Map<String, List<Employee>> top3PerDept = [Link]() .collect([Link](
Employee::department, [Link]( [Link](), list -> [Link]()
.sorted([Link](Employee::salary).reversed()) .limit(3)
.collect([Link]()) ) )); // Department with highest average salary:
[Link]() .collect([Link](Employee::department,
[Link](Employee::salary))) .entrySet().stream()
.max([Link]()) .ifPresent(e -> [Link]("Top dept: " +
[Link]() + " avg: " + [Link]())); // Flat list of names sorted by salary descending:
List<String> ranked = [Link]()
.sorted([Link](Employee::salary).reversed()) .map(Employee::name)
.collect([Link]());

Q: Find all duplicates in a list using Streams

List<Integer> numbers = [Link](1, 2, 3, 2, 4, 3, 5, 1); // Method 1: Using frequency


Set<Integer> duplicates = [Link]() .filter(n -> [Link](numbers, n) > 1)
.collect([Link]()); // {1, 2, 3} -- but O(n²)! // Method 2: Efficient with Set
(O(n)): Set<Integer> seen = new HashSet<>(); Set<Integer> dups = [Link]() .filter(n ->
![Link](n)) // add() returns false if already present .collect([Link]()); // Method
3: Count occurrences then filter: Map<Integer, Long> freq = [Link]()
.collect([Link](n -> n, [Link]())); Set<Integer> dupsFinal =
[Link]().stream() .filter(e -> [Link]() > 1) .map([Link]::getKey)
.collect([Link]());

Q: Design a Rate Limiter (Token Bucket algorithm)

import [Link]; import [Link];


public class TokenBucketRateLimiter { private final int capacity; // max tokens private final
int refillRate; // tokens per second private final AtomicLong tokens; private volatile long
lastRefillTime; public TokenBucketRateLimiter(int capacity, int refillRate) { [Link] =
capacity; [Link] = refillRate; [Link] = new AtomicLong(capacity);
[Link] = [Link](); } public synchronized boolean allowRequest()
{ refill(); if ([Link]() > 0) { [Link](); return true; } return false; //
rate limited } private void refill() { long now = [Link](); long elapsed =
now - lastRefillTime; long newTokens = (elapsed * refillRate) / 1000; if (newTokens > 0) {
[Link]([Link](capacity, [Link]() + newTokens)); lastRefillTime = now; } } } //
Per-user rate limiter: public class PerUserRateLimiter { private final
ConcurrentHashMap<String, TokenBucketRateLimiter> limiters = new ConcurrentHashMap<>(); public
boolean isAllowed(String userId) { TokenBucketRateLimiter limiter = [Link](
userId, id -> new TokenBucketRateLimiter(10, 2) // 10 capacity, 2/sec refill ); return
[Link](); } } // In Spring: use Resilience4j @RateLimiter or Bucket4j library for
production
QUICK REFERENCE -- LAST MINUTE CHEATSHEET

Java Collections Complexity


Collection get/contains add/put remove Thread-Safe

ArrayList O(1) O(1) amortized O(n) No

LinkedList O(n) O(1) O(1) if node known No

HashMap O(1) avg O(1) avg O(1) avg No

TreeMap O(log n) O(log n) O(log n) No

HashSet O(1) avg O(1) avg O(1) avg No

PriorityQueue O(n) O(log n) O(log n) No

ConcurrentHashMap O(1) avg O(1) avg O(1) avg Yes

CopyOnWriteArrayList O(1) O(n) O(n) Yes

HTTP Status Codes -- for REST API interviews


Code When to use

200 OK GET success, PUT/PATCH success with body

201 Created POST that creates a resource; include Location header

204 No Content DELETE success, PUT/PATCH success without body

400 Bad Request Validation error, malformed JSON

401 Unauthorized Missing or invalid authentication

403 Forbidden Authenticated but not authorized

404 Not Found Resource doesn't exist

409 Conflict Duplicate resource, optimistic lock conflict

422 Unprocessable Semantically invalid (business rule violation)

429 Too Many Rate limit exceeded


Requests

500 Internal Server Unhandled exception -- should never reach client in prod
Error

503 Service Circuit breaker open, or service down


Unavailable

Spring Boot Important Annotations


Annotation Purpose

@SpringBootApplication @Configuration + @EnableAutoConfiguration +


@ComponentScan

@RestController @Controller + @ResponseBody -- returns JSON by default

@RequestMapping / @GetMapping etc Map HTTP method + path to handler method

@Transactional Wraps method in DB transaction; default rollback on


RuntimeException
Annotation Purpose

@Cacheable / @CacheEvict Cache method return value; evict cache entry

@Scheduled(fixedRate=5000) Run method on schedule (needs @EnableScheduling)

@Async Run method in separate thread pool (needs @EnableAsync)

@ConditionalOnProperty Auto-config: only register bean if property set

@Profile Only load bean for specific profile (dev/prod)

@Value("${prop}") Inject property value

@ConfigurationProperties Bind property prefix to POJO (type-safe config)


GOOD LUCK IN YOUR INTERVIEW! YOU'VE GOT THIS.

Remember: Think out loud. Mention tradeoffs. Give real examples from your experience.

You might also like