0% found this document useful (0 votes)
2 views25 pages

Java Backend Interview Roadmap

This document serves as a comprehensive guide for preparing for senior Java backend interviews, covering essential topics such as behavioral questions, code review skills, core Java principles, concurrency, and system design. It includes a suggested four-week study plan, detailed explanations of key concepts, and common interview questions to practice. The guide emphasizes the importance of understanding architectural decisions and provides frameworks for effective communication during interviews.
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)
2 views25 pages

Java Backend Interview Roadmap

This document serves as a comprehensive guide for preparing for senior Java backend interviews, covering essential topics such as behavioral questions, code review skills, core Java principles, concurrency, and system design. It includes a suggested four-week study plan, detailed explanations of key concepts, and common interview questions to practice. The guide emphasizes the importance of understanding architectural decisions and provides frameworks for effective communication during interviews.
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

Cracking the Senior Java Backend Interview

Complete Roadmap: Behavioral, Code Review, Core Java, Concurrency & System Design (Flight Search /
Skyscanner-style)

Prepared for Interview Preparation

2026

Introduction & How To Use This Guide


Suggested Study Plan (4 Weeks)
Part A: Behavioral Round Preparation
A.1 Why Behavioral Rounds Matter So Much for Experienced Roles
A.2 The STAR-E Framework
A.3 Common Behavioral Questions (Grouped by Theme)
A.4 How to Talk About “Why This Architecture”
A.5 References — Behavioral
Part B: Code Review Skills
B.1 Why Code Review is Tested
B.2 The Code Review Checklist (Use This Mentally On Every Snippet)
B.3 Sample Code Review Walkthrough (Practice Exercise)
B.4 Common Code Review Interview Questions
B.5 References — Code Review & Clean Code
Part C: Core Java — OOP & SOLID Principles
C.1 Object-Oriented Programming Fundamentals
C.2 SOLID Principles — Deep Dive
Part D: Collections Framework — Deep Dive
D.1 HashMap Internals (Very Commonly Asked)
D.2 List Implementations
D.3 Set Implementations
D.4 Time Complexity Cheat Sheet
D.5 Common Collections Interview Questions
D.6 References — Collections
Part E: Memory Management & Garbage Collection
E.1 Heap vs Stack
E.2 Garbage Collection (GC)
E.3 Memory Leaks in Java (Yes, They Happen Even With GC!)
E.4 Strong, Weak, Soft, and Phantom References
E.5 Object Lifecycle
E.6 Common Memory Management Interview Questions
E.7 References — Memory & GC
Part F: Concurrency & Multithreading — Deep Dive
F.1 Race Conditions
F.2 Synchronization Mechanisms
F.3 Thread Safety Strategies
F.4 Executor Framework
F.5 CompletableFuture
F.6 ConcurrentHashMap (Revisited for Concurrency Context)
F.7 JVM Memory Model (JMM)
F.8 Deadlocks
F.9 Common Concurrency Interview Questions
F.10 References — Concurrency
Part G: System Design — Flight Search System (Skyscanner-style)
G.1 General System Design Interview Framework
G.2 Step 1: Requirements Gathering
G.3 Step 2: Scale Estimation (Sample Numbers)
G.4 Step 3: API Design
G.5 Step 4: High-Level Architecture
G.6 Step 5: Database Design
G.7 Step 6: Real-Time Availability — Deep Dive (Frequently Probed)
G.8 Step 7: Caching Strategy — Deep Dive
G.9 Step 8: Scalability Discussion
G.10 Common System Design Interview Questions (Flight Search & General)
G.11 General System Design Principles to Articulate
G.12 References — System Design
Part H: Final Preparation Checklist & Mock Interview Plan
H.1 Two-Week Final Sprint Checklist
H.2 Code Review Practice Routine
H.3 How Each Round Connects (Don’t Study in Isolation)
H.4 Day-of-Interview Tips
H.5 Master Reference List
Closing Note

Introduction & How To Use This Guide


This roadmap is built directly around a real 3-round senior Java backend interview structure (similar to
JPMorgan-style loops):

Round 1: Behavioral + Code Review + Core Java (Collections, Memory, OOP, SOLID)
Round 2: System Design (Flight Search System, similar to Skyscanner)
Round 3: Behavioral + Concurrency + Advanced Java

The guide is organized so you can study it top to bottom over 3-4 weeks, or jump to weak areas. Each major
topic includes:

1. Concept explanation (what you need to know, explained simply)


2. Why interviewers ask it (the underlying engineering judgment being tested)
3. Common interview questions (as many realistic questions as possible)
4. Sample strong answers / talking points
5. References for deeper study

Suggested Study Plan (4 Weeks)


Week Focus

Week 1 OOP, SOLID Principles, Clean Code, Code Review skills

Collections Framework, Memory Management, GC,


Week 2
References

Concurrency, Multithreading, Executor Framework, JVM


Week 3
Memory Model

System Design (Flight Search), Behavioral prep, Mock


Week 4
interviews

Part A: Behavioral Round Preparation

A.1 Why Behavioral Rounds Matter So Much for Experienced


Roles

For experienced engineers, interviewers assume you can code. What they are really testing is:

Do you own what you build, including its failures?


Can you explain technical decisions with reasoning, not just describe what happened?
Do you communicate clearly under follow-up pressure?
Can you reflect honestly on mistakes and what you’d change?

A.2 The STAR-E Framework

Use STAR-E: Situation, Task, Action, Result, Extension (what you’d do differently / what you learned). The
“Extension” is what separates senior candidates from junior ones — interviewers specifically probe “what
would you redesign today?”

A.3 Common Behavioral Questions (Grouped by Theme)

Ownership & Decision Making

1. Tell me about a project you owned end-to-end. What was your role?
2. Describe a technical decision you made that you later regretted. What would you do differently?
3. Tell me about a time you disagreed with your team’s technical direction. How did you handle it?
4. Describe the architecture of a system you built. Why did you choose that architecture over alternatives?
5. What alternatives did you consider for [your major project], and why did you reject them?
6. What bottlenecks did your system have, and how did you discover them?
7. If you had to redesign your current system today, what would you change and why?

Production Incidents & Failure Handling

8. Tell me about a production incident you were involved in. What happened and how did you respond?
9. Describe a time you caused a bug that affected production. How did you fix it and what did you learn?
10. How do you approach debugging a production issue under pressure?
11. Tell me about a time you had to make a trade-off between shipping fast and shipping correctly.

Teamwork & Collaboration


12. Tell me about a time you disagreed with a teammate’s code during a review. How did you communicate
feedback?
13. Describe a situation where you had to convince others to adopt your technical approach.
14. How do you mentor junior engineers or onboard new team members?
15. Tell me about a time you had to work with an ambiguous or incomplete requirement.

Learning & Growth

16. What is the most challenging technical concept you’ve had to learn recently?
17. Tell me about a time you were wrong about a technical assumption. How did you find out, and what did
you do?
18. What’s a recent technology or pattern you adopted, and why?

A.4 How to Talk About “Why This Architecture”

Interviewers in Round 3 specifically dig into why decisions were made. For every major project on your
resume, prepare a one-paragraph answer covering:

The constraint that drove the decision (scale, team size, deadline, existing infra)
The 2-3 alternatives you considered
The trade-off that made you pick what you picked (e.g., consistency vs. availability, cost vs. latency)
What broke or what you’d change with hindsight

Example structure: “We chose [X] because [constraint]. We also considered [Y] and [Z], but [Y] would have
required [cost] and [Z] didn’t satisfy [requirement]. In hindsight, [X] caused [issue] under [scale], and today
I’d consider [improvement].”

A.5 References — Behavioral

“Cracking the Coding Interview” — Behavioral chapter, Gayle Laakmann McDowell


Amazon Leadership Principles (widely used as a behavioral question bank):
[Link]
“The STAR Interview Method” — general career guides (Indeed, LinkedIn Learning)

Part B: Code Review Skills

B.1 Why Code Review is Tested

Senior engineers spend more time reviewing code than writing it from scratch. Interviewers want to see if
you can spot issues without being told what to look for — this mirrors real PR review.

B.2 The Code Review Checklist (Use This Mentally On Every


Snippet)

1. Correctness

Are there null pointer risks? (unchecked .get() , unvalidated inputs)


Are edge cases handled? (empty collections, zero/negative numbers, boundary indices)
Are there off-by-one errors in loops?
Does the code make incorrect assumptions about input (e.g., assuming a list is never empty,
assuming a map key always exists)?
Are exceptions caught appropriately or silently swallowed?
Are there resource leaks (unclosed streams, connections, files)? Should use try-with-resources.
Is there a possibility of integer overflow or type casting issues?

2. Code Quality / Clean Code

Naming: Are variable/method/class names descriptive and intention-revealing? ( processData() vs


applyDiscountToEligibleOrders() )

Method length & complexity: Is a method doing too many things (violating Single Responsibility)?
Can it be broken into smaller, named steps?
Magic numbers/strings: Are there hardcoded values that should be constants or config?
Duplication (DRY): Is the same logic repeated in multiple places?
Comments: Are comments explaining why (good) or restating what the code already says (bad/smell)?
Consistency: Does the code follow existing conventions in the codebase?

3. Design / SOLID & Architecture

Single Responsibility Principle (SRP): Does each class/method have one reason to change?
Open/Closed Principle: Can new behavior be added without modifying existing code (e.g., via
interfaces/strategy pattern instead of if-else chains on type)?
Liskov Substitution: Can subclasses be used interchangeably with their parent without breaking
behavior?
Interface Segregation: Are interfaces small and focused, or are classes forced to implement methods
they don’t need?
Dependency Inversion: Does the code depend on abstractions (interfaces) rather than concrete
implementations? Is dependency injection used appropriately?
Coupling & Cohesion: Is there unnecessary coupling between unrelated modules? Are related
behaviors grouped together?
Extensibility: If a new requirement came in (e.g., new payment type, new notification channel), how
many places would need to change?

4. Concurrency-Specific (Round 3 revisit)

Is shared mutable state accessed by multiple threads without synchronization?


Are collections used in a multi-threaded context thread-safe (e.g., HashMap vs ConcurrentHashMap )?
Could there be a race condition on a check-then-act pattern (e.g., if ([Link](k)) { [Link](k)...
} )?

Is synchronized used correctly — locking the right object, not too broad/narrow?
Are there potential deadlocks from lock ordering?

B.3 Sample Code Review Walkthrough (Practice Exercise)

Below is a typical “find the issues” snippet style. Practice reviewing code like this out loud, explaining issues
category by category.

public class OrderProcessor {


private Map<String, Integer> inventory = new HashMap<>();

public void processOrder(String itemId, int qty, double price) {


int current = [Link](itemId);
if (current >= qty) {
[Link](itemId, current - qty);
double total = price * qty;
if (total > 1000) {
total = total - (total * 0.1);
}
[Link]("Order processed: " + total);
} else {
[Link]("Not enough stock");
}
}
}

Issues to identify (practice articulating these):

1. Correctness: [Link](itemId) returns null if the item doesn’t exist → NullPointerException on auto-
unboxing to int .
2. Thread safety: HashMap is not thread-safe; if processOrder is called concurrently, there’s a race
condition between the check ( current >= qty ) and the update ( [Link] ) — classic check-then-act
bug. Should use ConcurrentHashMap with atomic operations like compute() , or synchronize the critical
section.
3. Magic number: 0.1 (10% discount) and 1000 threshold should be named constants or configurable.
4. Single Responsibility violation: the method does inventory management, pricing/discount logic, AND
output/logging — three responsibilities in one method. Should be split into InventoryService ,
PricingService , and a notification/logging concern.

5. No return value / no exception: the method returns void and uses [Link] for both
success and failure — callers can’t programmatically know the outcome. Should throw a custom
exception (e.g., InsufficientStockException ) or return a result object.
6. Extensibility: discount logic is hardcoded inline. If a new discount tier or promotional rule is added, this
method needs to be edited again — violates Open/Closed Principle. A DiscountStrategy interface would
allow new discount types to be added without modifying this class.

B.4 Common Code Review Interview Questions

1. What issues do you see in this code? (general open-ended)


2. How would you make this code more testable?
3. How would you refactor this to follow SOLID principles?
4. If this method needed to support three more discount types in the future, how would you change the
design?
5. Is this code thread-safe? If not, how would you fix it?
6. How would you handle the error case here — exception vs. return value vs. Optional?
7. What unit tests would you write for this method?
8. How would you reduce coupling between these two classes?

B.5 References — Code Review & Clean Code

“Clean Code” by Robert C. Martin (Uncle Bob) — naming, functions, SOLID chapters
“Effective Java” (3rd Edition) by Joshua Bloch — Items on API design, exceptions, generics
Google Java Style Guide: [Link]
Refactoring Guru — SOLID Principles: [Link]

Part C: Core Java — OOP & SOLID Principles

C.1 Object-Oriented Programming Fundamentals

Encapsulation

Bundling data (fields) and behavior (methods) together, and restricting direct access to internal state via
access modifiers ( private , protected ) and exposing controlled access through getters/setters or methods.

Why it matters: Protects invariants — e.g., a BankAccount class can ensure balance never goes negative by
controlling all mutations through a withdraw() method that validates first.

Abstraction

Hiding implementation details and exposing only essential features through interfaces or abstract classes.
The caller knows what an object does, not how.

Example: List<String> list = new ArrayList<>(); — code depends on the List interface (what), not ArrayList
internals (how).

Inheritance

A mechanism where a class (subclass) acquires properties and behavior of another class (superclass). Used
to model “is-a” relationships.

Risks: Overuse leads to fragile hierarchies — changes in the parent class can unexpectedly break subclasses
(“fragile base class problem”).

Polymorphism

The ability of an object to take many forms: - Compile-time (static): Method overloading — same method
name, different parameter lists. - Runtime (dynamic): Method overriding — subclass provides specific
implementation, resolved at runtime via dynamic dispatch (vtable lookup).

Composition vs Inheritance

Inheritance (“is-a”): Car extends Vehicle — tight coupling, subclass depends on superclass
implementation.
Composition (“has-a”): Car has an Engine field — looser coupling, behavior can be swapped at runtime
(Strategy pattern).

Rule of thumb interviewers look for: “Favor composition over inheritance” — composition gives more
flexibility, avoids deep fragile hierarchies, and supports the Open/Closed Principle better.

C.2 SOLID Principles — Deep Dive

Principle Definition Violation Smell Fix Pattern

A class should have God classes, methods doing Split into focused classes
S - Single
only one reason to validation + business logic + (e.g., Validator , Service ,
Responsibility
change persistence Repository )

Software entities
Strategy pattern,
should be open for Long if-else / switch on type that
O - Open/Closed polymorphism, plugin-
extension, closed for grows with each new feature
style interfaces
modification

Subclass throws
Subtypes must be UnsupportedOperationException for Rethink hierarchy; use
L - Liskov
substitutable for their inherited methods (e.g., Square composition or separate
Substitution
base types extends Rectangle breaking interfaces
setWidth / setHeight independence)

Clients shouldn’t be
I - Interface forced to depend on Large “fat” interfaces with many Split into smaller, role-
Segregation methods they don’t unrelated methods specific interfaces
use
High-level modules
shouldn’t depend on Constructor injection of
D - Dependency new ConcreteClass() hardcoded inside
low-level modules; interfaces (Spring
Inversion business logic
both depend on @Autowired -style DI)

abstractions

Common SOLID Interview Questions

1. Explain SOLID principles with real examples from your codebase.


2. Give an example of a Liskov Substitution Principle violation and how you’d fix it.
3. How does Dependency Injection relate to the Dependency Inversion Principle?
4. How would you refactor a large switch statement on an enum type to follow Open/Closed Principle?
5. What’s the difference between an abstract class and an interface, and when would you choose one over
the other?
6. Can you give an example where you applied composition over inheritance?

Part D: Collections Framework — Deep Dive

D.1 HashMap Internals (Very Commonly Asked)

How HashMap Works

Backed by an array of buckets ( Node[] table ).


put(key, value) :

1. Compute hashCode() of key, apply hash() function (XOR of hashCode with its upper 16 bits shifted
right) to spread bits and reduce collisions.
2. index = hash & ([Link] - 1) — works because table length is always a power of 2.
3. If bucket is empty, place new Node there.
4. If bucket has entries (collision), traverse the linked list (or tree) — if key matches (via equals() ),
update value; else append.
Java 8+ improvement: If a bucket’s linked list grows beyond a threshold ( TREEIFY_THRESHOLD = 8 ), it
converts to a red-black tree for that bucket, improving worst-case lookup from O(n) to O(log n).
Resizing: When size > capacity * loadFactor (default load factor 0.75), the table doubles in size and all
entries are rehashed.

Hash Collisions

Occur when two different keys produce the same bucket index. Handled via chaining (linked list / tree per
bucket). Poor hashCode() implementations (e.g., always returning 0) cause all entries to land in one bucket —
degrading to O(n) lookups.

equals() and hashCode() Contract

If two objects are equal ( equals() returns true), they must have the same hashCode() .
The reverse is not required (different objects can share a hash, causing collisions, which is fine).
Violating this contract breaks HashMap / HashSet / HashTable behavior — e.g., you put() an object as a key,
then get() with an “equal” object but it isn’t found because hashCode differs.

ConcurrentHashMap

Thread-safe alternative to HashMap without locking the entire map.


Java 7: Used segment-based locking (lock striping) — the map was divided into segments, each
independently lockable.
Java 8+: Removed segments; uses CAS (Compare-And-Swap) operations for most updates and
synchronized blocks only on individual bin/bucket heads when there’s a collision — much finer-
grained locking.
Reads are largely lock-free (using volatile reads).
Supports atomic operations like compute() , computeIfAbsent() , merge() — important for avoiding race
conditions in the check-then-act pattern.
size() is approximate under concurrent modification (eventually consistent).

HashMap vs Hashtable vs ConcurrentHashMap vs LinkedHashMap vs TreeMap

Type Thread-Safe Ordering Null keys/values Notes

Fastest for
1 null key, multiple null
HashMap No No order guaranteed single-
values
threaded

Legacy,
Hashtable Yes (full lock) No order No nulls rarely used
now

Preferred
for
ConcurrentHashMap Yes (fine-grained) No order No nulls
concurrent
access

Useful for
Insertion order (or access
LinkedHashMap No 1 null key LRU
order with LRU config)
caches

O(log n)
Sorted by key operations,
TreeMap No No null keys
(Comparable/Comparator) red-black
tree

D.2 List Implementations

ArrayList vs LinkedList

Aspect ArrayList LinkedList

Backing structure Dynamic array Doubly linked list

get(index) O(1) O(n)

add(end) O(1) amortized O(1)

add(middle) / remove(middle) O(n) (shifting) O(n) to find + O(1) to splice

Higher (node pointers, ~2 extra


Memory overhead Lower (contiguous array)
references per element)

Cache locality Better (contiguous memory) Worse (scattered nodes)

Implements List , RandomAccess List , Deque , Queue

Practical takeaway: ArrayList is the default choice for almost all use cases due to better cache
performance, even for insertions, unless you specifically need Deque behavior (frequent additions/removals
at both ends).

D.3 Set Implementations


Type Ordering Backing Notes

O(1) average
HashSet No order HashMap internally
operations

Slight overhead over


LinkedHashSet Insertion order LinkedHashMap internally
HashSet

O(log n) operations,
Red-black tree (TreeMap
TreeSet Sorted order supports NavigableSet
internally)
(floor, ceiling, etc.)

D.4 Time Complexity Cheat Sheet

Operation ArrayList LinkedList HashMap TreeMap HashSet TreeSet

O(1) avg,
O(log n)
get/contains O(1)/O(n) O(n) O(log n) O(1) avg O(log n)
worst (Java
8 treeify)

add O(1) amortized O(1) O(1) avg O(log n) O(1) avg O(log n)

remove O(n) O(1)/O(n) to find O(1) avg O(log n) O(1) avg O(log n)

D.5 Common Collections Interview Questions

1. Explain how HashMap works internally, including how put() and get() resolve collisions.
2. What changed in HashMap / ConcurrentHashMap between Java 7 and Java 8?
3. Why must equals() and hashCode() be overridden together? What breaks if you only override one?
4. When would you choose LinkedList over ArrayList ? Be honest about real trade-offs.
5. How does TreeMap maintain sorted order — what’s the underlying data structure and complexity?
6. What happens during HashMap resizing? What is the load factor and why is the default 0.75?
7. How would you implement an LRU cache using Java collections? (Hint: LinkedHashMap with
removeEldestEntry override, or HashMap + doubly linked list)
8. What is the difference between Comparable and Comparator ?
9. Why is HashMap not thread-safe — what specifically can go wrong (e.g., during resize with concurrent
puts — infinite loop bug in Java 7)?
10. What’s the difference between fail-fast and fail-safe iterators? Give examples ( ArrayList iterator vs
ConcurrentHashMap iterator).

D.6 References — Collections

“Effective Java” by Joshua Bloch — Chapters on generics, collections, equals/hashCode


Java official docs — HashMap source comments:
[Link]
Java official docs — ConcurrentHashMap:
[Link]
“Java Concurrency in Practice” by Brian Goetz — Chapter 5 (Building Blocks)

Part E: Memory Management & Garbage


Collection
E.1 Heap vs Stack

Aspect Stack Heap

Local variables, method call frames, Objects, instance variables,


Stores
references arrays

Managed by Garbage
Lifetime Tied to method call (LIFO)
Collector

Thread-safety Each thread has its own stack Shared across all threads

Smaller, fixed (can cause Larger, can cause


Size
StackOverflowError ) OutOfMemoryError

Slower (more complex


Speed Faster (simple push/pop)
allocation/GC)

Key point: A local variable Person p = new Person(); — the reference p lives on the stack, but the actual
Person object lives on the heap. When the method returns, p is popped off the stack, but the object remains

on the heap until garbage collected (if no other references exist).

E.2 Garbage Collection (GC)

What GC Does

Automatically reclaims memory occupied by objects that are no longer reachable from any GC root (active
thread stacks, static variables, JNI references).

Generational Heap Structure

Most JVM GCs (G1, Parallel, CMS) divide the heap into generations based on the observation that most
objects are short-lived:

Young Generation: Split into Eden (new objects allocated here) and two Survivor spaces (S0, S1).
Minor GC runs here frequently and is fast.
Old Generation (Tenured): Objects that survive multiple minor GCs get promoted here. Major/Full
GC runs here, less frequently but more expensively.
Metaspace (Java 8+, replaced PermGen): Stores class metadata, method info.

Common GC Algorithms

Serial GC: Single-threaded, suitable for small applications.


Parallel GC: Multi-threaded for young/old gen, throughput-focused (default in older Java versions).
CMS (Concurrent Mark Sweep): Reduces pause times by doing most work concurrently with
application threads (deprecated in Java 9+).
G1 (Garbage First): Default since Java 9. Divides heap into regions, prioritizes collecting regions with
most garbage first, aims for predictable pause times.
ZGC / Shenandoah: Ultra-low-latency collectors (sub-millisecond pauses), used for very large heaps.

Minor GC vs Major/Full GC

Minor GC: Cleans Young Generation, generally fast (milliseconds).


Major/Full GC: Cleans Old Generation (and sometimes everything), can cause noticeable pause (“stop-
the-world”) affecting application latency — a key production performance concern.

E.3 Memory Leaks in Java (Yes, They Happen Even With GC!)
Common causes: 1. Static collections that keep growing — e.g., a static Map<String, Object> cache that’s
never evicted. 2. Unclosed resources — database connections, streams, file handles not closed (use try-
with-resources). 3. Listener/callback registrations never deregistered — the listener holds a reference to
the object, preventing GC. 4. Inner classes holding implicit reference to outer class — a non-static
inner class or anonymous class retains a reference to its enclosing instance, even if logically unrelated. 5.
ThreadLocal misuse — values not removed after use in thread pools (threads are reused, ThreadLocal
values linger). 6. String interning of large/unbounded user-generated strings — fills the string pool.

E.4 Strong, Weak, Soft, and Phantom References

Reference Type GC Behavior Use Case

Never collected while reference


Strong (default, e.g., Object o = new Object() ) Normal usage
exists

Collected only when JVM is low on


Soft ( SoftReference ) Memory-sensitive caches
memory (before OutOfMemoryError )

WeakHashMap , avoiding
Collected at next GC cycle if no
Weak ( WeakReference ) memory leaks in
strong references exist
caches/listeners

Resource cleanup tracking


Object already finalized; reference
Phantom ( PhantomReference ) (replacement for
enqueued for cleanup actions
finalize() )

Practical example: WeakHashMap is often used for caches where you want entries to be automatically
removed once the key object is no longer referenced elsewhere — prevents memory leaks from “forgotten”
cache entries.

E.5 Object Lifecycle

1. Allocation: Object created via new , memory allocated in Eden space.


2. Usage: Object referenced and used by application code.
3. Unreachable: All references to the object go out of scope or are set to null .
4. Garbage Collection: GC identifies unreachable objects during a mark phase.
5. Finalization (legacy, discouraged): finalize() method called before reclamation — deprecated since
Java 9, avoid using it; prefer try-with-resources / Cleaner API.
6. Reclamation: Memory returned to the heap for reuse.

E.6 Common Memory Management Interview Questions

1. Explain the difference between heap and stack memory.


2. What is the difference between Minor GC and Major/Full GC? How do they affect application latency?
3. How would you diagnose a memory leak in a production Java application? (Tools: heap dumps, jmap ,
VisualVM, Eclipse MAT, profilers)
4. What are Strong, Weak, Soft, and Phantom references? Give a real use case for each.
5. Can a Java application have a memory leak despite having a garbage collector? Give examples.
6. What is the difference between G1 GC and CMS? Why was G1 made the default?
7. What causes a StackOverflowError vs an OutOfMemoryError ? Give scenarios for each.
8. How does ThreadLocal cause memory leaks in thread pool environments, and how do you prevent it?
9. What is Metaspace, and how is it different from PermGen?
10. Walk through the lifecycle of an object from creation to garbage collection.

E.7 References — Memory & GC


“Java Performance: The Definitive Guide” by Scott Oaks — GC chapters
Oracle’s GC Tuning Guide: [Link]
Baeldung — Java Garbage Collection: [Link]
Baeldung — WeakReference/SoftReference: [Link]

Part F: Concurrency & Multithreading — Deep


Dive

F.1 Race Conditions

A race condition occurs when multiple threads access shared mutable state, and the outcome depends on
the timing/interleaving of their execution.

Classic Example: Lost Update

class Counter {
private int count = 0;
public void increment() {
count++; // NOT atomic! This is read-modify-write across 3 steps
}
}

count++ is actually three operations: read count , add 1, write back. If two threads interleave between read
and write, one increment can be lost.

Check-Then-Act Race Conditions

if (![Link](key)) { // Thread A checks: key not present


[Link](key, value); // Thread B also checked, both put — but maybe only one should have
}

Fix: Use [Link](key, value) or computeIfAbsent() — atomic operations.

F.2 Synchronization Mechanisms

synchronized Keyword

Method-level: public synchronized void method() — locks on this (or the Class object for static methods).
Block-level: synchronized(lockObject) { ... } — more granular, allows locking on a specific object rather
than the whole instance.
Internally uses the object’s monitor (intrinsic lock). Only one thread can hold a monitor at a time;
others block.
Reentrant: A thread holding a lock can re-acquire it (e.g., calling another synchronized method on the
same object).

Locks ( [Link] )

ReentrantLock — explicit lock with more capabilities than synchronized : - tryLock() — attempt to acquire
without blocking indefinitely (with optional timeout). - lockInterruptibly() — allows the waiting thread to be
interrupted. - Fairness option — can configure FIFO ordering of waiting threads. - Must manually unlock() in
a finally block — risk of forgetting, unlike synchronized which auto-releases.
ReadWriteLock / ReentrantReadWriteLock: - Allows multiple readers OR one writer at a time — useful
when reads vastly outnumber writes (e.g., a configuration cache).

When synchronized vs ReentrantLock

Use synchronized when Use ReentrantLock when

Simple mutual exclusion needed Need tryLock() / timeout

Want automatic lock release Need fairness guarantees

Code simplicity preferred Need to interrupt waiting threads

Need separate read/write locks

F.3 Thread Safety Strategies

1. Immutability: Immutable objects (all fields final , no setters, defensive copies of mutable fields) are
inherently thread-safe — no shared mutable state to corrupt. String , Integer , records (Java 16+) are
immutable.

2. Confinement / Stateless Design: Don’t share state between threads — e.g., each request gets its
own local variables (stack-confined), or use ThreadLocal for per-thread state.

3. Concurrent Collections: ConcurrentHashMap , CopyOnWriteArrayList , BlockingQueue implementations —


designed for safe concurrent access without external synchronization.

4. Synchronization: Protect shared mutable state with locks when the above approaches aren’t feasible.

CopyOnWriteArrayList

Creates a new copy of the underlying array on every write ( add , remove ). Reads are lock-free and never see
partial updates. Good for read-heavy, write-rare scenarios (e.g., list of event listeners).

F.4 Executor Framework

Why Not Just Use new Thread() ?

Creating threads is expensive (OS-level resource), and unbounded thread creation can exhaust system
resources. The Executor framework manages a pool of reusable threads.

Thread Pool Types ( Executors factory methods)

Factory Method Behavior Use Case

Fixed number of threads, Predictable, CPU-bound


newFixedThreadPool(n)
unbounded queue workloads

Short-lived async tasks, but


Creates threads as needed,
newCachedThreadPool() risk of unbounded thread
reuses idle ones, unbounded
creation

One thread, tasks execute


newSingleThreadExecutor() Tasks that must run in order
sequentially

Supports delayed/periodic
newScheduledThreadPool(n) Cron-like tasks
execution

Uses ForkJoinPool , work-


newWorkStealingPool() CPU-intensive parallel tasks
stealing for parallelism
Production note: [Link]() and unbounded queues in newFixedThreadPool can cause
OutOfMemoryError under load. In production, prefer constructing ThreadPoolExecutor directly with bounded
queues and a defined RejectedExecutionHandler .

ThreadPoolExecutor Core Parameters

new ThreadPoolExecutor(
corePoolSize, // threads kept alive even if idle
maximumPoolSize, // max threads allowed
keepAliveTime, // idle time before extra threads die
[Link],
workQueue, // BlockingQueue for pending tasks
rejectionHandler // what to do when pool+queue are full
);

F.5 CompletableFuture

Enables asynchronous, non-blocking composition of tasks (introduced in Java 8).

Key Methods

supplyAsync(() -> ...)— run a task asynchronously, returns a result.


thenApply(fn) — transform result (sync, on same thread as completion).
thenApplyAsync(fn) — transform result on a (possibly different) thread from the executor.

thenCompose(fn) — chain dependent async operations (like flatMap for futures).


thenCombine(other, fn) — combine results of two independent futures.
exceptionally(fn) / handle(fn) — error handling.
allOf(...) / anyOf(...) — wait for multiple futures.

Example

CompletableFuture<Flight> flightFuture = [Link](() -> fetchFlight(id), executor);


CompletableFuture<Price> priceFuture = [Link](() -> fetchPrice(id), executor);

CompletableFuture<FlightDetails> combined = [Link](priceFuture,


(flight, price) -> new FlightDetails(flight, price));

This pattern is directly relevant to the Flight Search system design — fetching flight info and pricing from
different services in parallel.

F.6 ConcurrentHashMap (Revisited for Concurrency Context)

Java 8+: lock-free reads, CAS-based writes, synchronized only on bin heads during collisions.
Atomic compound operations: putIfAbsent , compute , computeIfAbsent , computeIfPresent , merge — essential
for avoiding race conditions without external locks.

F.7 JVM Memory Model (JMM)

Visibility Problem

Without proper synchronization, one thread’s writes to a variable may not be visible to another thread due to
CPU caching and compiler reordering optimizations.

volatile Keyword
Guarantees visibility: writes to a volatile variable are immediately visible to all threads (no caching in
CPU registers/local caches).
Establishes a happens-before relationship: all writes before a volatile write are visible to any thread
that reads that volatile variable after.
Does NOT guarantee atomicity for compound operations ( volatile int count; count++; is still not atomic).

happens-before Relationship

A guarantee that memory writes by one specific statement are visible to another specific statement.
Established by: - synchronized block entry/exit - volatile variable read/write - Thread start() and join() -
[Link] utilities (locks, atomic classes)

Atomic Classes ( [Link] )

AtomicInteger , AtomicLong , AtomicReference — provide lock-free, thread-safe operations using CAS (Compare-
And-Swap) at the hardware level.

AtomicInteger counter = new AtomicInteger(0);


[Link](); // atomic, no lock needed

F.8 Deadlocks

Occur when two or more threads are blocked forever, each waiting for a resource held by the other.

Classic scenario: Thread A locks Resource1, then tries to lock Resource2. Thread B locks Resource2, then
tries to lock Resource1. Neither can proceed.

Prevention strategies: - Lock ordering: Always acquire locks in a consistent global order. - Lock
timeout: Use tryLock(timeout) instead of blocking indefinitely. - Avoid nested locks where possible; use
higher-level concurrency utilities.

F.9 Common Concurrency Interview Questions

1. What is a race condition? Give a concrete code example and explain how to fix it.
2. Explain the difference between synchronized and ReentrantLock . When would you choose one over the
other?
3. What does the volatile keyword guarantee, and what does it NOT guarantee?
4. Walk through how ConcurrentHashMap achieves thread safety without locking the entire map.
5. What is a deadlock? How do you detect and prevent it?
6. Explain CompletableFuture — how would you fetch data from two services in parallel and combine results?
7. What is the difference between Runnable and Callable ?
8. What happens if you don’t bound your thread pool’s queue in production?
9. How would you design a thread-safe cache that’s read-heavy but occasionally updated?
(CopyOnWriteArrayList / ReadWriteLock / ConcurrentHashMap)
10. Explain the happens-before relationship and why it matters.
11. What is the difference between wait()/notify() and Lock / Condition ?
12. How does the Fork/Join framework achieve parallelism, and what is “work stealing”?
13. If you revisit the OrderProcessor code review example (Part B.3) — how would you make processOrder

thread-safe?

F.10 References — Concurrency

“Java Concurrency in Practice” by Brian Goetz — the definitive book on this topic
Oracle Java Concurrency Tutorial: [Link]
Baeldung — Java Concurrency Collection: [Link]
Baeldung — CompletableFuture Guide: [Link]

Part G: System Design — Flight Search System


(Skyscanner-style)

G.1 General System Design Interview Framework

Always follow this sequence — interviewers explicitly evaluate your process, not just the final diagram:

1. Clarify Requirements (Functional + Non-Functional)


2. Estimate Scale (users, QPS, data volume)
3. Define APIs (endpoints, request/response shapes)
4. High-Level Architecture (major components and how they interact)
5. Database Design (entities, schema, indexing)
6. Deep Dives (caching, real-time data, scalability — pick 2-3 areas interviewer cares about)
7. Trade-offs & Evolution (what would you change at 10x scale?)

G.2 Step 1: Requirements Gathering

Functional Requirements

Users can search flights by source, destination, date(s), passengers.


Users can filter results (airline, price range, number of stops, departure time window).
Users can sort results (price, duration, departure time).
Users can view seat availability and fare details.
(Optionally) Users can book a flight — but for a “search system,” booking may be out of scope unless
asked.

Non-Functional Requirements

High read throughput: Search is read-heavy (millions of searches vs. relatively few bookings).
Low latency: Search results should return in ~200-500ms even with multiple airline partner calls.
High availability: Search should degrade gracefully if a partner API is slow/down (partial results better
than no results).
Eventual consistency acceptable for search results (price/availability can be slightly stale), but
booking must be strongly consistent (no overbooking).
Scalability: Handle traffic spikes (holiday seasons, flash sales).

Clarifying Questions to Ask the Interviewer

Are we aggregating flights from multiple airlines/partners (like Skyscanner/aggregator model), or is this
a single airline’s own search system?
Do we need to support booking, or just search/comparison?
What’s the expected scale — number of daily active users, searches per second?
Do we need real-time pricing, or is slightly stale pricing (cache) acceptable?

G.3 Step 2: Scale Estimation (Sample Numbers)

Metric Example Estimate


Daily Active Users 10 million

Searches per user per day ~3

Total searches/day 30 million

Peak QPS (search) ~30M / 86400 * peak factor (5x) ≈ 1700 QPS

Read:Write ratio ~1000:1 (search reads vs. bookings)

Flight routes (global) ~50,000 active routes

Partner airlines 100s of airlines/GDS systems

Why this matters: These numbers justify your architecture choices — e.g., heavy caching, read replicas,
CDN for static data (airport/airline metadata).

G.4 Step 3: API Design

Search Endpoint

GET /flights/search
Query Params:
- source (IATA code, e.g., BOM)
- destination (IATA code, e.g., BLR)
- departureDate (YYYY-MM-DD)
- returnDate (optional, for round trips)
- passengers (adults, children, infants)
- cabinClass (economy, business, etc.)
- airlines (optional filter, comma-separated)
- priceMin, priceMax (optional filter)
- maxStops (optional filter)
- sortBy (price | duration | departureTime)
- page, pageSize (pagination)

Sample Response Structure

{
"searchId": "uuid-for-this-search-session",
"results": [
{
"flightId": "AI202-BOM-BLR-2026-07-01",
"airline": "Air India",
"departureTime": "2026-07-01T06:00:00+05:30",
"arrivalTime": "2026-07-01T08:00:00+05:30",
"duration": "PT2H",
"stops": 0,
"price": { "amount": 4500, "currency": "INR" },
"availableSeats": 12,
"fareClass": "ECONOMY"
}
],
"pagination": { "page": 1, "pageSize": 20, "totalResults": 134 },
"filters": { "airlines": ["Air India", "IndiGo"], "priceRange": [3000, 12000] }
}

Other Endpoints

GET /flights/{flightId} -> details of a specific flight


GET /airports/search?query=Mum -> autocomplete for airport search
GET /flights/{flightId}/availability -> real-time seat availability
POST /bookings -> create a booking (separate bounded context)
API Design Discussion Points

Pagination: Offset-based ( page , pageSize ) is simple but can have consistency issues with changing
result sets; cursor-based pagination is more robust for large/dynamic datasets, but offset is usually
acceptable for flight search given bounded result sizes per route.
Idempotency: Search is naturally idempotent (GET). Booking ( POST /bookings ) needs an idempotency
key to prevent duplicate bookings on retry.
Error Handling: Use standard HTTP status codes — 400 for invalid params (e.g., invalid airport code),
503 if downstream partner systems are unavailable (with partial results returned rather than total

failure if possible).
Versioning: /v1/flights/search to allow API evolution without breaking existing clients.

G.5 Step 4: High-Level Architecture

Major Components

1. API Gateway — entry point, handles auth, rate limiting, routing.


2. Search Service — orchestrates search requests, queries internal flight database + calls partner airline
APIs.
3. Aggregation/Composition Layer — merges results from multiple sources (internal inventory +
external partner GDS systems like Amadeus/Sabre), normalizes formats, deduplicates.
4. Pricing Service — computes final prices including fees, taxes, discounts.
5. Inventory/Availability Service — tracks real-time seat availability for internally-managed inventory.
6. Cache Layer (Redis/Memcached) — caches frequent search results, airport/airline metadata.
7. Search Database — stores flight schedules, routes, fares (read-optimized, possibly
denormalized/Elasticsearch for search).
8. Partner Integration Layer — adapters for each airline/GDS partner, with circuit breakers for
resilience.
9. Notification/Event Bus (Kafka) — for async events like price changes, inventory updates.

Request Flow (Search)

1. Client → API Gateway → Search Service.


2. Search Service checks cache for this route+date combination.
3. If cache miss (or partial), Search Service queries internal flight DB and fans out to Partner
Integration Layer (parallel calls to multiple airline partners using CompletableFuture /async).
4. Aggregation layer merges, deduplicates, normalizes results.
5. Pricing Service applies final pricing/discounts.
6. Results cached (with short TTL) and returned to client, paginated/sorted/filtered.

G.6 Step 5: Database Design

Key Entities

Airport: code (IATA), name, city, country, coordinates.


Airline: code, name, alliance info.
Route: source airport, destination airport, distance, typical duration.
Flight: flight number, airline, route, departure/arrival times, aircraft type, schedule (recurring or specific
date).
Fare: flight reference, cabin class, base price, taxes, fare rules (refundable, baggage allowance).
Inventory: flight + date reference, total seats, booked seats, available seats per cabin class.

Schema Considerations
Flight schedules are largely static (change infrequently) — good candidate for heavy caching / read
replicas / even a search-optimized store like Elasticsearch for the search-by-route-and-date queries.
Inventory/Availability changes frequently (every booking) — needs to be in a transactional store
(relational DB like PostgreSQL/MySQL) with strong consistency guarantees, or a dedicated inventory
service.
Separate read path (search, denormalized, cached, eventually consistent) from write path (booking,
normalized, strongly consistent) — this is essentially CQRS (Command Query Responsibility
Segregation).

Indexing Strategy

Composite index on (source, destination, departureDate) — the most common search pattern.
Index on airline for filtering.
For full-text/autocomplete on airport/city names, use a search engine like Elasticsearch rather than DB
LIKE queries.
Be mindful: too many indexes slow down writes (inventory updates) — balance read optimization on
flight/fare tables vs. write optimization on inventory tables.

G.7 Step 6: Real-Time Availability — Deep Dive (Frequently


Probed)

The Core Challenge

Seat availability changes continuously (bookings happen every second across potentially millions of seats),
but search needs to show “reasonably current” availability without hitting the live inventory system on every
search (too slow, too much load).

Approaches

1. Event-Driven Updates - When inventory changes (booking, cancellation), the Inventory Service
publishes an event (via Kafka) — SeatAvailabilityChanged . - Search cache / read replicas subscribe and update
asynchronously. - Trade-off: slight staleness (seconds) in search results — acceptable, since actual
availability is reconfirmed at booking time.

2. Partner Synchronization (for aggregator model) - Partner airlines periodically push availability
updates (via webhooks or scheduled pulls) — frequency depends on partner SLAs (could be every few
minutes for low-demand routes, near-real-time for high-demand routes). - Use tiered sync frequency:
popular routes sync more often than obscure ones.

3. Two-Phase Confirmation at Booking Time - Search results show “indicative” availability (from
cache/last sync). - When the user proceeds to book, a real-time availability check is made directly to the
airline/inventory system — this is the authoritative check. - If unavailable at this point, show
“price/availability changed” — a familiar UX pattern on real booking sites.

Preventing Overbooking — Concurrent Booking Problem

Scenario: Two users simultaneously try to book the last seat.

Solutions: - Optimistic Locking: Inventory row has a version column. Booking transaction reads version,
attempts update WHERE version = X ; if 0 rows affected (someone else updated first), retry or fail gracefully.
Good for low-contention scenarios. - Pessimistic / Distributed Locking: Acquire a lock (e.g., via Redis
SETNX with TTL, or a distributed lock manager like Redisson/Zookeeper) on the specific flight+fareclass
inventory record before decrementing seat count. Higher contention overhead but guarantees correctness. -
Database-level atomic decrement with constraint: UPDATE inventory SET available = available - 1 WHERE
flight_id = ? AND available > 0 — atomic conditional update; if 0 rows affected, no seats available. Simple and

effective for single-DB setups. - Eventual Consistency + Compensation: Allow slight overbooking, detect
via reconciliation, and compensate (refund/rebook) — used by some airlines for overbooking strategies
intentionally, but NOT something to propose for “preventing overbooking” unless discussing real airline
practices.

Best answer for interview: Combine atomic conditional DB updates (or distributed lock for cross-service
inventory) for the booking path, with eventual consistency + events for the search/display path. Be
explicit that search-time availability is “advisory” and booking-time is “authoritative.”

G.8 Step 7: Caching Strategy — Deep Dive

What to Cache

Popular routes (e.g., Mumbai-Bangalore, Delhi-Mumbai) — high read frequency, results change
relatively slowly.
Airport and airline metadata — essentially static, cache with long TTL (hours/days) or even serve
from CDN.
Search result pages — cache by (source, destination, date, filters-hash) key with short TTL (e.g., 1-5
minutes).

What NOT to Cache (or cache very carefully)

Real-time seat availability for high-demand/last-minute flights — staleness here directly impacts
user trust (“I searched and it showed available, but booking failed”).
Personalized pricing (if loyalty discounts, location-based pricing apply) — caching needs to be keyed
per user-segment, not globally.

Cache Invalidation Strategies

TTL-based expiry: Simple, predictable, but can serve stale data briefly.
Event-based invalidation: When inventory/price changes significantly, publish an event to invalidate
relevant cache keys (e.g., all cached searches for that flight).
Write-through vs. write-around: For flight schedule data, write-through (update cache on write)
keeps cache fresh; for high-churn inventory, write-around (cache only populated on read) with short TTL
avoids cache thrashing.

Trade-offs: Freshness vs. Performance

Cheaper/longer cache TTL → better performance, more risk of stale availability shown to users
(mitigated by booking-time re-verification).
The interviewer wants you to articulate this explicitly: “We accept some staleness in search results
because the booking flow re-verifies availability — this lets us cache aggressively and keep search
latency low.”

G.9 Step 8: Scalability Discussion

Load Balancing

Layer 7 load balancer (e.g., NGINX, ALB) distributes traffic across Search Service instances.
Use consistent hashing if routing to specific cache shards based on route/date.

Horizontal Scaling

Search Service is stateless → easy to scale horizontally by adding more instances behind the load
balancer.
Partner Integration Layer can also scale horizontally; use circuit breakers (e.g., Hystrix/Resilience4j
pattern) so a slow partner doesn’t exhaust thread pools across all instances.

Read Replicas

Flight schedule/fare database: multiple read replicas to handle the heavy read load from search; writes
(schedule updates, new routes) go to primary.
Inventory database: may need fewer replicas since writes (bookings) are more frequent relative to reads
compared to schedule data, but read replicas can still serve “advisory” availability for search.

Database Partitioning (Sharding)

Shard by geography/region (e.g., domestic routes per country, international routes separately) —
most searches are regional.
Alternatively shard by route hash — distributes load evenly but can complicate cross-region queries
(e.g., multi-leg international itineraries).

Distributed Caching

Redis Cluster for shared cache across Search Service instances — avoids cold caches per instance and
redundant partner API calls.
CDN for static assets (airport images, airline logos, metadata).

High Availability

Multi-AZ / multi-region deployment for the Search Service and cache layer.
Graceful degradation: if partner APIs are down, return cached/internal-inventory-only results rather than
failing the entire search — communicate to user that “some results may be limited.”

G.10 Common System Design Interview Questions (Flight Search


& General)

Flight Search Specific

1. How would you design the search API for a flight search system? What query parameters and response
structure would you use?
2. How do you handle real-time seat availability without hitting the inventory DB on every search?
3. How would you prevent overbooking when two users try to book the last seat simultaneously?
4. What would you cache, and what would you never cache, in this system? Why?
5. How would you design the database schema for flights, fares, and inventory?
6. How would you handle a partner airline’s API being slow or down during a search request?
7. How would you support filtering and sorting efficiently at scale (e.g., sort by price across 10,000
results)?
8. How would you scale this system to handle a 10x traffic spike during a holiday sale?
9. How would you design for international flights with multiple legs/connections (multi-city itineraries)?
10. How would you handle currency conversion and region-specific pricing?

General System Design (Practice These Too — Mentioned in Prep Tips)

11. Design a URL Shortener (focus: hashing/encoding strategy, redirect performance, analytics).
12. Design a Payment System (focus: idempotency, consistency, reconciliation, fraud checks).
13. Design a Notification Platform (focus: fan-out, multiple channels — push/email/SMS, rate limiting,
retries).
14. Design a Ride-Sharing Service (focus: geo-indexing for driver matching, real-time location updates,
surge pricing).
G.11 General System Design Principles to Articulate

Start broad, then go deep where the interviewer steers — don’t try to cover everything equally;
watch for follow-up cues.
Always state assumptions explicitly (“I’m assuming X searches/day; if it’s 100x that, here’s what I’d
change…”).
CAP theorem awareness: search = favor Availability + Partition tolerance (AP, eventual consistency
okay); booking = favor Consistency (CP, can’t overbook).
Talk about monitoring/observability briefly — metrics (latency, error rate per partner), alerting on
partner API failures.
Mention trade-offs explicitly — there’s rarely a “correct” answer, only good reasoning about trade-
offs.

G.12 References — System Design

“Designing Data-Intensive Applications” by Martin Kleppmann — caching, consistency, partitioning


chapters
“System Design Interview” Vol 1 & 2 by Alex Xu
ByteByteGo / High Scalability blog (general system design patterns): [Link]
Martin Fowler — CQRS pattern: [Link]
Resilience4j docs (circuit breaker pattern): [Link]

Part H: Final Preparation Checklist & Mock


Interview Plan

H.1 Two-Week Final Sprint Checklist

Days 1-3: Core Java + Collections

Can explain HashMap internals (hashing, collisions, treeification, resizing) without notes
Can explain ConcurrentHashMap’s locking evolution (Java 7 segments vs Java 8 CAS)
Can compare ArrayList vs LinkedList with honest trade-offs
Can explain equals()/hashCode() contract and consequences of violating it
Can explain all 5 SOLID principles with a personal project example for each

Days 4-6: Memory & GC

Can explain heap vs stack with an example


Can describe generational GC (young/old gen, minor vs major GC)
Can explain all 4 reference types (strong/weak/soft/phantom) with use cases
Can describe 3 realistic memory leak scenarios from production code

Days 7-9: Concurrency

Can write a race condition example and fix it live


Can explain synchronized vs ReentrantLock vs ReadWriteLock with when-to-use
Can explain volatile and happens-before
Can use CompletableFuture to parallelize two independent calls
Can explain deadlock causes and prevention (lock ordering)

Days 10-12: System Design (Flight Search)


Can draw the high-level architecture from memory in under 5 minutes
Can design the search API with realistic query params and response shape
Can explain the overbooking prevention approach with at least 2 alternatives
Can explain caching strategy with explicit freshness vs. performance trade-offs
Can discuss scaling (sharding, read replicas, load balancing) for 10x growth

Days 13-14: Behavioral + Mock Interviews

Prepared STAR-E stories for: ownership, conflict, failure/incident, technical decision, mentoring
Can answer “what would you redesign today” for your top 2 projects
Do at least 2 full mock interviews (record yourself if possible)

H.2 Code Review Practice Routine

Every day, pick ONE small Java code snippet (from open-source PRs, or write your own buggy snippet) and
review it out loud against the checklist in Part B.2: 1. Correctness issues 2. Code quality issues 3.
Design/SOLID issues 4. Concurrency issues (if applicable)

Speaking your review out loud — as if to a colleague — builds the communication muscle interviewers
specifically evaluate.

H.3 How Each Round Connects (Don’t Study in Isolation)

Notice the interview is interconnected: - The Round 1 code review snippet often resurfaces in Round 3
with a concurrency angle — so when reviewing code, always ask “is this thread-safe?” even in Round 1. -
System design (Round 2) draws on Collections/Concurrency knowledge — e.g., choosing ConcurrentHashMap for
a cache, or CompletableFuture for parallel partner API calls. - Behavioral questions in Round 3 often probe the
same projects discussed in Round 1 — be consistent and deepen your answers rather than repeating them
verbatim.

H.4 Day-of-Interview Tips

Think out loud — interviewers can’t read your mind; narrate your reasoning even when uncertain.
Ask clarifying questions early — especially in system design, don’t assume scale/requirements.
It’s fine to say “I’m not 100% sure, but here’s my reasoning” — better than guessing confidently
and being wrong.
Manage time — in a 60-minute round, don’t spend 40 minutes on requirements gathering for system
design; aim for ~10 minutes on requirements/API, ~20-25 on architecture/DB, ~15-20 on deep dives
(caching/availability/scaling).
Connect answers back to production experience — “In my experience with [X], we handled this
by…” adds credibility.

H.5 Master Reference List

Topic Resource

“Clean Code” — Robert C. Martin; Refactoring Guru


OOP, SOLID, Clean Code
([Link]

Effective Java practices “Effective Java” 3rd Ed. — Joshua Bloch

Java 17 official docs


Collections internals
([Link]

Concurrency “Java Concurrency in Practice” — Brian Goetz; Oracle


Concurrency Tutorial

“Java Performance: The Definitive Guide” — Scott Oaks; Oracle


Memory/GC
GC Tuning Guide

“Designing Data-Intensive Applications” — Martin Kleppmann;


System Design
“System Design Interview” Vol 1/2 — Alex Xu; ByteByteGo blog

“Cracking the Coding Interview” behavioral chapter; Amazon


Behavioral
Leadership Principles

General Java practice Baeldung ([Link]

Closing Note
This guide mirrors the exact structure of the real interview experience it was built from: behavioral depth,
practical code review, Java internals (collections/memory/concurrency), and a realistic large-scale system
design. The candidates who succeed in these interviews aren’t the ones who memorize definitions — they’re
the ones who can reason out loud about trade-offs, own their past decisions critically, and connect
Java fundamentals to real production concerns.

Good luck.

You might also like