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

Java_Core_Advanced_Interview_Mastery

The 'Java Core & Advanced Interview Mastery Guide' by Aman Mishra is a comprehensive resource designed to help candidates prepare for Java backend interviews, covering topics from fundamentals to advanced concepts. It includes over 100 real interview questions, detailed explanations of Java internals, and practical tips for understanding key concepts. The guide is structured to facilitate learning through concept explanations followed by interview questions, making it suitable for developers at all experience levels.

Uploaded by

swethapidapa
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 views32 pages

Java_Core_Advanced_Interview_Mastery

The 'Java Core & Advanced Interview Mastery Guide' by Aman Mishra is a comprehensive resource designed to help candidates prepare for Java backend interviews, covering topics from fundamentals to advanced concepts. It includes over 100 real interview questions, detailed explanations of Java internals, and practical tips for understanding key concepts. The guide is structured to facilitate learning through concept explanations followed by interview questions, making it suitable for developers at all experience levels.

Uploaded by

swethapidapa
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 CORE & ADVANCED

Interview Mastery Guide

The only guide you need to crack Java interviews at any level.
From fundamentals to advanced internals - explained simply.

✓ 100+ Real Interview Questions with Detailed Answers

✓ Java Collections Internals - How They Really Work

✓ Multithreading & Concurrency - Made Simple

✓ JVM Internals - Memory, GC, ClassLoading

✓ Java 8 to 21 - Features Interviewers Actually Ask

✓ Common Mistakes & How to Avoid Them

✓ One-Page Cheat Sheets for Quick Revision

by Aman Mishra

Software Engineer | 6+ Years Backend Experience

Java | Spring Boot | PostgreSQL | Kafka | Docker


Java Core & Advanced - Interview Mastery Guide by Aman Mishra

About This Guide

Hey there! I am Aman Mishra, and I have spent 6+ years building backend systems with Java. During
this time, I have given and taken hundreds of interviews. I know exactly what gets asked, what
confuses people, and what separates a good answer from a great one.

This guide is NOT just another collection of questions. I have designed it so that you actually
understand the concepts. When you understand the 'why' behind things, you can answer any
question an interviewer throws at you - even the ones you have never seen before.

Who is this for?


• Anyone preparing for Java backend interviews (freshers to 8+ years)
• Developers who use Java daily but want to understand it deeper
• People who are tired of memorizing and want to actually learn

How to use this guide


Each chapter starts with clear concept explanations, followed by interview questions. I have marked
questions by difficulty: [Easy], [Medium], and [Hard]. Read the concepts first, then try answering
questions before reading my answers. The cheat sheets at the end are perfect for last-minute revision
before your interview.

Pro Tip: Don't just read. Try to explain each concept out loud as if you're in an interview. If you
can explain it simply, you truly understand it.

Page 2
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Table of Contents

Chapter 1: Java Fundamentals That Interviewers Love


• OOP Concepts - The Right Way to Explain Them
• String, StringBuilder, StringBuffer - The Classic Question
• Immutability - Why It Matters So Much
• equals() and hashCode() Contract
• Exception Handling - Beyond Try-Catch

Chapter 2: Collections Framework - Deep Dive


• How HashMap Really Works Internally
• HashMap vs ConcurrentHashMap vs Hashtable
• ArrayList vs LinkedList - The Real Story
• TreeMap, LinkedHashMap and When to Use What
• Fail-Fast vs Fail-Safe Iterators

Chapter 3: Multithreading & Concurrency


• Thread Lifecycle and States
• synchronized, volatile, and Atomic Classes
• ExecutorService and Thread Pools
• CompletableFuture - Async Programming
• Common Concurrency Problems and Solutions
• Producer-Consumer, Deadlock, Race Conditions

Chapter 4: JVM Internals


• JVM Architecture - How Java Code Actually Runs
• Memory Model - Heap, Stack, Metaspace
• Garbage Collection - Types and Tuning
• ClassLoading Mechanism
• JVM Tuning Flags You Should Know

Chapter 5: Java 8 to 21 - Modern Java Features


• Functional Interfaces and Lambda Expressions
• Stream API - Operations, Collectors, Parallel Streams
• Optional - The Right Way to Use It
• Records, Sealed Classes, Pattern Matching

Page 3
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

• Virtual Threads (Project Loom) - Java 21

Chapter 6: Design Patterns & Best Practices


• Singleton - Why Interviewers Still Ask This
• Factory, Builder, Strategy, Observer
• SOLID Principles with Real Examples
• Immutable Class Design
• Writing Clean, Interview-Ready Code

Chapter 7: Tricky Interview Questions & Gotchas


• Output-Based Questions
• Find the Bug Questions
• Scenario-Based Questions
• Questions That Catch Even Senior Developers

Bonus: Cheat Sheets & Quick Revision


• Collections Cheat Sheet
• Concurrency Cheat Sheet
• JVM & GC Cheat Sheet
• Java 8+ Features Cheat Sheet
• Top 30 One-Liner Interview Answers

Page 4
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 1: Java Fundamentals That


Interviewers Love

1.1 OOP Concepts - The Right Way to Explain Them


Every Java interview starts here. But most candidates give textbook definitions that sound robotic.
Here is how to explain OOP so the interviewer knows you actually get it.

Encapsulation
Think of encapsulation as putting your data inside a protective box. You have private fields and public
methods to access them. The key idea is: the outside world does not need to know how you store
data internally. They just use the methods you give them.

Real example: A BankAccount class has a private balance field. You cannot directly change it. You
must call deposit() or withdraw() which have validation logic. This way, nobody can set the balance to
-1000 directly.

public class BankAccount { private double balance; // hidden from outside public
void deposit(double amount) { if (amount > 0) balance += amount; // validation! }
public double getBalance() { return balance; // controlled access } }

Inheritance
Inheritance lets you create a new class based on an existing one. The child class gets all the
non-private members of the parent and can add its own behavior or override the parent's behavior.

But here is what interviewers want to hear: Inheritance represents an 'IS-A' relationship. A Dog
IS-A Animal. If the relationship does not make sense as 'IS-A', you should probably use composition
instead (HAS-A). This is a very common follow-up question.

COMMON MISTAKE: Many candidates say 'inheritance is for code reuse.' That is only partly
true. If you just want code reuse without an IS-A relationship, use composition. Saying this in an
interview shows maturity.

Polymorphism
Polymorphism means 'many forms.' In Java, it comes in two flavors:

• Compile-time (Method Overloading): Same method name, different parameters. The


compiler decides which method to call based on the argument types.
• Runtime (Method Overriding): Child class provides its own version of a parent's method. The
JVM decides which method to call at runtime based on the actual object type.

Page 5
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Animal animal = new Dog(); // Reference: Animal, Object: Dog [Link](); //


Calls Dog's speak(), NOT Animal's // This is runtime polymorphism in action!

TIP: When explaining polymorphism, always mention the difference between reference type and
object type. This shows the interviewer you understand how method dispatch works in Java.

Abstraction
Abstraction means hiding complex implementation details and showing only what is necessary. In
Java, you achieve it through abstract classes and interfaces. Think of it like driving a car - you use the
steering wheel and pedals (interface), but you do not need to know how the engine works internally
(implementation).

[Easy] Q: What is the difference between abstraction and encapsulation?


A: Encapsulation is about data hiding - wrapping data and methods together and controlling access
through access modifiers. Abstraction is about implementation hiding - showing only the relevant
features and hiding the complexity. Encapsulation is HOW you achieve data protection. Abstraction
is WHAT you choose to expose. A simple way to remember: encapsulation hides data, abstraction
hides complexity.

[Medium] Q: When would you use an abstract class over an interface?


A: Use an abstract class when: (1) you want to share code among closely related classes, (2) you
need non-public members or constructors, (3) you need to maintain state (instance variables). Use
an interface when: (1) unrelated classes need to implement the same behavior (like Serializable), (2)
you want to define a contract without caring about implementation, (3) you need multiple inheritance
of type. Since Java 8, interfaces can have default methods, which blurred the line. But the key
difference remains: abstract classes can have constructors and maintain state; interfaces
fundamentally define capabilities.

[Hard] Q: Can you explain the diamond problem and how Java handles it?
A: The diamond problem occurs when a class inherits from two classes that both have the same
method. Java avoids this by not allowing multiple class inheritance. However, with Java 8 default
methods, a similar issue arises with interfaces. If two interfaces have the same default method and a
class implements both, the compiler forces you to override that method explicitly. You can choose
which one to call using [Link](). This design keeps things unambiguous while still
allowing flexible interface composition.

1.2 String, StringBuilder, StringBuffer - The Classic Question


This question appears in almost every Java interview. Let me explain it in a way that sticks with you
forever.

String is Immutable

Page 6
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

When you create a String, it cannot be changed. Every time you 'modify' a String, Java actually
creates a brand new String object. The old one sits in memory until garbage collection picks it up.

String s = "Hello"; s = s + " World"; // Creates a NEW String object // "Hello" is


still in memory, s now points to "Hello World"

Why is String immutable?

• String Pool: Java caches Strings in a special memory area called the String Pool. If Strings
were mutable, changing one would affect all references pointing to it.
• Security: Strings are used for file paths, network connections, class loading. If someone could
modify them after creation, it would be a huge security risk.
• Thread Safety: Since Strings cannot change, they are automatically thread-safe. Multiple
threads can share the same String without any synchronization.
• Hashcode Caching: Since String is immutable, its hashcode is calculated once and cached.
This makes Strings very efficient as HashMap keys.

StringBuilder vs StringBuffer
Both are mutable - you can modify them without creating new objects. The only difference is thread
safety:

• StringBuilder: NOT thread-safe, but faster. Use this in 99% of cases.


• StringBuffer: Thread-safe (methods are synchronized), but slower. Use only when multiple
threads are modifying the same buffer - which is rare.
// BAD - Creates many temporary String objects String result = ""; for (int i = 0; i
< 10000; i++) { result += i; // New String object every iteration! } // GOOD - Uses
single mutable buffer StringBuilder sb = new StringBuilder(); for (int i = 0; i <
10000; i++) { [Link](i); // Modifies same object } String result = [Link]();

[Easy] Q: What is the String Pool?


A: The String Pool (or String Intern Pool) is a special memory area inside the Heap where Java
stores String literals. When you create a String using a literal like String s = "Hello", Java first checks
if "Hello" already exists in the pool. If yes, it returns the same reference. If no, it creates a new entry.
This saves memory when the same String is used multiple times. Note: Strings created with 'new'
keyword go to the regular heap, not the pool, unless you explicitly call .intern().

[Medium] Q: What happens when you do String s = new String("Hello")?


A: Two objects may be created: one in the String Pool (for the literal "Hello", if it does not already
exist) and one in the regular heap (because of the 'new' keyword). The variable 's' points to the heap
object, not the pooled one. This is why new String("Hello") == "Hello" returns false - they are different
objects in different memory locations. But new String("Hello").equals("Hello") returns true because
equals() compares content.

[Hard] Q: How does String concatenation work internally? Does the compiler optimize it?
A: Yes, the compiler is smart about this. For compile-time constants like "Hello" + " World", the
compiler directly creates "Hello World" - no runtime concatenation. For variables, before Java 9, the

Page 7
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

compiler used StringBuilder behind the scenes. From Java 9 onwards, it uses invokedynamic with
StringConcatFactory, which is more flexible and allows the JVM to pick the best strategy at runtime.
However, in a loop, each iteration still creates a new StringBuilder (pre-Java 9) or makes a new call,
so you should still use StringBuilder explicitly for loop concatenation.

1.3 Immutability - Why It Matters So Much


Immutability is one of those topics that separates junior developers from senior ones in interviews. An
immutable object is one whose state cannot be modified after creation.

How to Create an Immutable Class


• Make the class final (so it cannot be extended)
• Make all fields private and final
• Do not provide setter methods
• If a field is a mutable object (like List or Date), return a defensive copy in the getter
• Initialize all fields via the constructor
public final class Employee { private final String name; private final List<String>
skills; public Employee(String name, List<String> skills) { [Link] = name;
[Link] = new ArrayList<>(skills); // defensive copy } public String getName() {
return name; } public List<String> getSkills() { return
[Link](skills); // defensive copy } }

COMMON MISTAKE: The most common mistake candidates make: forgetting about defensive
copies. If your immutable class has a List field and you return it directly, the caller can modify it
through the reference. Always return an unmodifiable copy. Interviewers LOVE catching people
on this.

[Medium] Q: Why is immutability important in a multithreaded environment?


A: Immutable objects are inherently thread-safe because their state never changes after creation.
Multiple threads can read them simultaneously without any synchronization, locks, or coordination.
This eliminates entire categories of concurrency bugs like race conditions and visibility issues. This is
exactly why String, Integer, and other wrapper classes are immutable - they are used everywhere
and making them thread-safe by default avoids countless bugs.

1.4 equals() and hashCode() Contract


This is a critical topic. If you get this wrong in an interview, it is almost always a rejection for mid-level
and above positions. Let me make it crystal clear.

The Contract
• If two objects are equal (equals() returns true), they MUST have the same hashCode.
• If two objects have the same hashCode, they are NOT necessarily equal (hash collision).

Page 8
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

• If you override equals(), you MUST override hashCode(). Breaking this contract will cause
HashMaps and HashSets to behave incorrectly.

What Happens If You Break It?


// If equals() says obj1 == obj2 but hashCodes differ: Map<MyObject, String> map =
new HashMap<>(); [Link](obj1, "value"); [Link](obj2); // Returns NULL even though
[Link](obj2)! // Because HashMap checks hashCode first to find the bucket. //
Different hashCode = different bucket = never finds it.

TIP: In interviews, always mention that modern IDEs and libraries like Lombok can generate
equals() and hashCode() for you. But you should understand what they do, because follow-up
questions always go deeper.

[Medium] Q: Write a proper equals() and hashCode() for an Employee class with id and
name.
A: The equals() method should: (1) check if it is the same reference (==), (2) check if null or different
class, (3) cast and compare fields. For hashCode(), use [Link]() with the same fields used in
equals(). Critical rule: use the same fields in both methods. If equals() uses id and name, hashCode()
must also use id and name. Using fewer fields in hashCode() is technically legal but reduces
HashMap performance. Using more fields breaks the contract.

@Override public boolean equals(Object o) { if (this == o) return true; if (o ==


null || getClass() != [Link]()) return false; Employee e = (Employee) o; return
id == [Link] && [Link](name, [Link]); } @Override public int hashCode() {
return [Link](id, name); }

1.5 Exception Handling - Beyond Try-Catch


Most candidates can explain try-catch. But interviewers want to know the deeper concepts.

Exception Hierarchy
Everything extends Throwable. It has two children: Error (OutOfMemoryError, StackOverflowError -
you should NOT catch these) and Exception. Exception has two types: Checked (IOException,
SQLException - compiler forces you to handle) and Unchecked/Runtime (NullPointerException,
ArrayIndexOutOfBounds - compiler does not force handling).

[Easy] Q: What is the difference between checked and unchecked exceptions?


A: Checked exceptions are checked at compile time. The compiler forces you to either catch them or
declare them with 'throws'. They represent recoverable conditions like file not found or network
failure. Unchecked exceptions (RuntimeException and its subclasses) are not checked at compile
time. They represent programming bugs like null pointer or array index out of bounds. The philosophy
is: you should fix bugs in code rather than catching them.

[Medium] Q: What happens if both catch and finally blocks throw exceptions?

Page 9
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

A: The exception from the finally block replaces the one from the catch block. The original exception
is lost! This is actually a common source of bugs. The best practice is to never throw from a finally
block. Use try-with-resources instead, which handles this properly through the suppressed exception
mechanism - the original exception is preserved and the finally-block exception is attached as a
suppressed exception.

[Hard] Q: Explain try-with-resources and the AutoCloseable interface.


A: Try-with-resources (Java 7+) automatically closes resources that implement AutoCloseable.
Resources are closed in reverse order of declaration. If both the try block and close() throw, the try
block exception is primary and the close() exception becomes suppressed (accessible via
getSuppressed()). This is the correct way to handle any resource that needs closing - database
connections, streams, network sockets, etc. Since Java 9, you can even use effectively final
variables in try-with-resources without re-declaring them.

// Modern way - clean and safe try (var conn = [Link](); var stmt
= [Link](sql); var rs = [Link]()) { while ([Link]()) { /*
process */ } } // All 3 resources auto-closed in reverse order

Page 10
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 2: Collections Framework - Deep


Dive

Collections is the most heavily tested topic in Java interviews. If you understand how HashMap works
internally, you are already ahead of 80% of candidates.

2.1 How HashMap Really Works Internally


Let me walk you through what happens step by step when you do [Link](key, value).

Step 1: Calculate Hash


HashMap calls [Link]() and then applies an internal hash function that spreads the bits to
reduce collisions. This is called 'perturbation' - it XORs the hash with itself shifted right by 16 bits: hash
= hashCode ^ (hashCode >>> 16).

Step 2: Find Bucket Index


The bucket index is calculated as: index = hash & (capacity - 1). This is equivalent to hash % capacity
but faster because capacity is always a power of 2. This is why HashMap's capacity is always a power
of 2!

Step 3: Handle Collision


If the bucket is empty, a new Node is placed there. If occupied (collision), HashMap uses chaining -
nodes are added to a linked list at that bucket. Since Java 8, if a bucket has more than 8 nodes AND
the total capacity is at least 64, the linked list converts to a Red-Black Tree. This improves worst-case
lookup from O(n) to O(log n).

Step 4: Resize (Rehashing)


When the number of entries exceeds capacity x loadFactor (default 0.75), HashMap doubles its
capacity and rehashes all entries. This is expensive! If you know the approximate size, always set the
initial capacity to avoid rehashing: new HashMap<>(expectedSize / 0.75 + 1).

Key Insight: HashMap works best when hashCode() distributes keys evenly across buckets. A
bad hashCode() that returns the same value for all keys turns HashMap into a linked list (or
tree), destroying its O(1) performance.

[Medium] Q: What happens if two different keys have the same hashCode in a HashMap?
A: This is called a hash collision. Both keys end up in the same bucket. HashMap stores them as a
linked list (or tree if more than 8 nodes). When you do get(key), HashMap first goes to the right

Page 11
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

bucket using hashCode, then walks through the list/tree and uses equals() to find the exact match.
This is why both hashCode() and equals() must be correctly implemented for HashMap keys to work.

[Hard] Q: Why did Java 8 introduce treeification in HashMap? When does it happen?
A: Before Java 8, a malicious user could craft keys with the same hashCode to cause all entries to
land in one bucket, creating O(n) lookups - a HashDoS attack. Java 8 converts long chains (more
than 8 nodes) to Red-Black Trees, giving O(log n) worst case. Treeification happens when: (1)
bucket size exceeds TREEIFY_THRESHOLD (8), AND (2) table capacity is at least
MIN_TREEIFY_CAPACITY (64). If capacity is less than 64, it resizes instead of treeifying. Trees
convert back to lists when they shrink below UNTREEIFY_THRESHOLD (6).

[Hard] Q: What is the time complexity of HashMap operations in best, average, and worst
case?
A: Best case: O(1) for get/put/remove - when there are no collisions. Average case: O(1) - with a
good hash function and load factor of 0.75, collisions are rare. Worst case: O(log n) since Java 8
(due to treeification). Before Java 8, worst case was O(n) because long chains were just linked lists.
Note: Resizing is O(n) but happens infrequently, so amortized cost stays O(1).

2.2 HashMap vs ConcurrentHashMap vs Hashtable


This comparison comes up in almost every interview. Here is the clear picture:

Feature HashMap ConcurrentHashMap Hashtable

Thread-safe? No Yes Yes

Null keys? One null key No nulls No nulls

Null values? Multiple No nulls No nulls

Locking None Segment/Bucket level Entire map

Performance Fastest (single thread) Good (concurrent) Poor (full lock)

Iterator Fail-fast Weakly consistent Fail-fast

Since Java 1.2 Java 1.5 Java 1.0 (legacy)

[Medium] Q: Why is ConcurrentHashMap preferred over Hashtable?


A: Hashtable locks the entire map for every operation - even reads block other reads.
ConcurrentHashMap (since Java 8) uses a much smarter approach: it locks only the specific bucket
being modified, and reads are mostly lock-free using volatile reads. This means multiple threads can
read and write simultaneously to different buckets. In practice, ConcurrentHashMap can be 10-100x
faster than Hashtable under concurrent access. Hashtable is considered legacy - there is no reason
to use it in modern Java.

2.3 ArrayList vs LinkedList - The Real Story

Page 12
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

The textbook answer is: 'ArrayList for random access, LinkedList for frequent insertions.' But the
reality is more nuanced.

• ArrayList stores elements in a contiguous array. Random access is O(1). Adding at the end is
amortized O(1). Inserting/removing in the middle is O(n) because elements must be shifted.
• LinkedList stores elements as nodes with pointers. Accessing by index is O(n) because you
must traverse from the head. Adding/removing at the beginning or end is O(1). But each element
has overhead of two extra pointers (next and prev).

The truth is: ArrayList is almost always better. Even for insertions in the middle, ArrayList's
cache locality (elements are next to each other in memory) makes it faster than LinkedList in
practice, despite the O(n) shifting. LinkedList's scattered memory layout causes frequent cache
misses. Use LinkedList only when you need it as a Queue or Deque.

[Medium] Q: When would you actually use a LinkedList over ArrayList?


A: In practice, rarely. The main use case is when you need a Queue (FIFO) or Deque (double-ended
queue) because LinkedList implements both interfaces. For pure List usage, ArrayList wins in almost
all scenarios because of CPU cache friendliness. Even the Java documentation hints at this. If an
interviewer asks this, mentioning cache locality and memory overhead shows deep understanding.

2.4 TreeMap, LinkedHashMap and When to Use What


Choosing the right Map implementation shows your design sense:

• HashMap: Use when order does not matter and you want the fastest operations. O(1) average.
• LinkedHashMap: Use when you need insertion order preserved. Perfect for implementing
LRU cache (with accessOrder=true). Slightly slower than HashMap due to linked list overhead.
• TreeMap: Use when you need keys in sorted order. Based on Red-Black Tree. Operations are
O(log n). Great when you need range queries like subMap(), headMap(), tailMap().

[Medium] Q: How would you implement an LRU Cache in Java?


A: The simplest way is to extend LinkedHashMap with accessOrder=true and override
removeEldestEntry(). When accessOrder is true, accessing an element moves it to the end. The
removeEldestEntry() method is called after every put - you return true when size exceeds the
capacity, which removes the least recently accessed element (the head). This gives you a fully
working LRU cache in about 10 lines of code.

public class LRUCache<K, V> extends LinkedHashMap<K, V> { private final int
capacity; public LRUCache(int capacity) { super(capacity, 0.75f, true); //
accessOrder = true! [Link] = capacity; } @Override protected boolean
removeEldestEntry([Link]<K,V> eldest) { return size() > capacity; } }

2.5 Fail-Fast vs Fail-Safe Iterators


This is a favorite follow-up question after collections discussions.

Page 13
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

• Fail-Fast: Iterators from HashMap, ArrayList, HashSet, etc. They throw


ConcurrentModificationException if the collection is modified while iterating. They track a
modCount internally - if it changes during iteration, they fail immediately.
• Fail-Safe (Weakly Consistent): Iterators from ConcurrentHashMap, CopyOnWriteArrayList.
They work on a snapshot or allow concurrent modification. They never throw
ConcurrentModificationException but may not reflect the latest changes.

[Easy] Q: How do you safely remove elements from a List while iterating?
A: Three safe ways: (1) Use [Link]() - this is the classic way. (2) Use
[Link](predicate) since Java 8 - cleaner and more readable. (3) Use a
CopyOnWriteArrayList if concurrent access is needed. Never use a regular for-each loop and call
[Link]() - this will throw ConcurrentModificationException.

// Safe removal using removeIf (Java 8+) [Link](item ->


[Link]("temp")); // Safe removal using Iterator Iterator<String> it =
[Link](); while ([Link]()) { if ([Link]().startsWith("temp")) {
[Link](); // Safe! } }

Page 14
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 3: Multithreading & Concurrency

Concurrency questions are where interviews get serious. This is the topic that most clearly separates
junior from senior candidates. Let me break it down simply.

3.1 Thread Lifecycle and States


A thread in Java goes through these states:

• NEW: Thread is created but start() has not been called yet.
• RUNNABLE: Thread is ready to run or currently running. The OS scheduler decides when it
actually runs.
• BLOCKED: Thread is waiting to acquire a monitor lock (trying to enter a synchronized block).
• WAITING: Thread is waiting indefinitely for another thread's action (wait(), join(), park()).
• TIMED_WAITING: Thread is waiting for a specified time (sleep(), wait(timeout), join(timeout)).
• TERMINATED: Thread has finished execution or was killed by an exception.

[Easy] Q: What is the difference between start() and run()?


A: start() creates a new OS thread and calls run() in that new thread. If you call run() directly, it
executes in the current thread like a normal method - no new thread is created. This is a very
common beginner mistake and interviewers use it to quickly check your understanding.

[Medium] Q: What is the difference between BLOCKED and WAITING states?


A: BLOCKED means the thread wants to enter a synchronized block but another thread holds the
lock. It will automatically unblock when the lock becomes available. WAITING means the thread has
explicitly given up execution (by calling wait(), join(), or park()) and can only proceed when another
thread explicitly wakes it up (notify(), notifyAll(), unpark()). The key difference: BLOCKED is
involuntary waiting for a lock; WAITING is voluntary waiting for a signal.

3.2 synchronized, volatile, and Atomic Classes


synchronized
The synchronized keyword ensures that only one thread can execute a block of code at a time. It
provides both mutual exclusion (only one thread at a time) and visibility (changes made by one
thread are visible to others). It works by acquiring a monitor lock on an object.

volatile
The volatile keyword ensures visibility only. When a variable is volatile, any read always gets the
latest value from main memory, and any write goes directly to main memory. But volatile does NOT

Page 15
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

provide atomicity - it does not prevent race conditions on compound operations like count++.

volatile boolean running = true; // Visibility guarantee // Thread 1 while (running)


{ /* work */ } // Thread 2 running = false; // Thread 1 will see this immediately //
But this is NOT safe with volatile: volatile int count = 0; count++; // This is
read-modify-write, NOT atomic!

Atomic Classes
AtomicInteger, AtomicLong, AtomicReference, etc. use CPU-level CAS (Compare-And-Swap)
operations to provide both atomicity and visibility without locking. They are faster than synchronized
for simple operations like incrementing a counter.

AtomicInteger counter = new AtomicInteger(0); [Link](); //


Thread-safe, lock-free! [Link](5, 10); // Set to 10 only if current
value is 5

[Hard] Q: When would you use volatile vs Atomic vs synchronized?


A: Use volatile when: one thread writes and others only read a simple flag (like a 'stop' flag). Use
Atomic classes when: multiple threads do simple atomic operations (counters, references). Use
synchronized when: you need to protect compound operations or multiple variables that must change
together atomically. Rule of thumb: volatile for flags, Atomic for counters, synchronized for everything
else. Each level adds more safety but also more overhead.

3.3 ExecutorService and Thread Pools


Creating threads manually is expensive. Thread pools reuse a fixed set of threads to execute many
tasks. ExecutorService is the modern way to handle this.

Common Thread Pool Types


• FixedThreadPool: Fixed number of threads. Best for CPU-bound tasks. If all threads are busy,
new tasks wait in a queue.
• CachedThreadPool: Creates new threads as needed, reuses idle ones. Good for short-lived
async tasks. Warning: can create unlimited threads!
• ScheduledThreadPool: For tasks that run after a delay or periodically.
• SingleThreadExecutor: One thread processes tasks sequentially. Guarantees order of
execution.
• WorkStealingPool (Java 8+): Uses ForkJoinPool. Threads steal tasks from other threads'
queues when idle. Good for parallel computation.
ExecutorService executor = [Link](10); // Submit a task that
returns a result Future<String> future = [Link](() -> { return
fetchDataFromDB(); }); String result = [Link](); // Blocks until result is ready
// Always shut down! [Link](); [Link](30,
[Link]);

Page 16
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

COMMON MISTAKE: Never use [Link]() in production without


understanding the risks. Under load, it can create thousands of threads and crash your
application with OutOfMemoryError. In production, always use ThreadPoolExecutor directly with
explicit bounds.

[Hard] Q: How would you configure a ThreadPoolExecutor for a production application?


A: Use ThreadPoolExecutor directly: set corePoolSize to the number of CPUs for CPU-bound tasks,
or higher (2x-4x CPUs) for I/O-bound tasks. Set maximumPoolSize with a reasonable limit. Use a
bounded queue (ArrayBlockingQueue) to prevent memory issues. Define a RejectionHandler
(CallerRunsPolicy is often safest). Set meaningful thread names using a custom ThreadFactory for
debugging. Monitor the pool with getActiveCount(), getQueueSize(), etc. The formula for I/O-bound
tasks: threads = CPUs x (1 + waitTime/computeTime).

3.4 CompletableFuture - Async Programming


CompletableFuture is a game-changer introduced in Java 8. It lets you write non-blocking,
asynchronous code that is readable and composable.

// Chain async operations [Link](() -> fetchUser(userId))


.thenApply(user -> enrichWithOrders(user)) .thenApply(user ->
calculateLoyaltyScore(user)) .thenAccept(user -> sendEmail(user)) .exceptionally(ex
-> { [Link]("Failed", ex); return null; }); // Combine multiple futures
CompletableFuture<User> userFuture = fetchUserAsync(id);
CompletableFuture<List<Order>> ordersFuture = fetchOrdersAsync(id);
CompletableFuture<Profile> profile = userFuture .thenCombine(ordersFuture, (user,
orders) -> new Profile(user, orders));

[Medium] Q: What is the difference between thenApply, thenAccept, and thenRun?


A: thenApply takes a Function - it receives the result and returns a new value (transformation).
thenAccept takes a Consumer - it receives the result but returns nothing (side effect like logging).
thenRun takes a Runnable - it does not receive the result and returns nothing (just run some code
after completion). Think of it as: Apply transforms, Accept consumes, Run just executes.

[Hard] Q: What is the difference between thenApply and thenApplyAsync?


A: thenApply runs the function in the same thread that completed the previous stage.
thenApplyAsync submits the function to the ForkJoinPool (or a specified executor) to run in a
different thread. Use thenApply for lightweight transformations. Use thenApplyAsync for heavy or
blocking operations so you don't block the completing thread. You can also pass a custom executor
to thenApplyAsync to control which thread pool runs it.

3.5 Common Concurrency Problems and Solutions


Deadlock
Deadlock happens when two threads each hold a lock and wait for the other's lock. Neither can
proceed.

Page 17
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

// Thread 1: locks A, then tries to lock B // Thread 2: locks B, then tries to lock
A // DEADLOCK! Both are stuck forever. // Prevention: Always acquire locks in the
same order! // Or use tryLock() with timeout.

Race Condition
When the outcome depends on the timing of thread execution. Classic example: two threads
incrementing a shared counter without synchronization.

Starvation
A thread never gets CPU time because higher-priority threads keep taking it. Solution: use fair locks
(new ReentrantLock(true)).

[Hard] Q: How would you detect and prevent deadlocks in a production system?
A: Detection: Use jstack or JMX to get thread dumps - they show deadlocks automatically. You can
also use [Link]() programmatically. Prevention: (1) Always acquire
locks in a consistent global order. (2) Use tryLock() with timeouts instead of lock(). (3) Minimize the
scope of synchronized blocks. (4) Use higher-level concurrency utilities (ConcurrentHashMap,
AtomicReference) instead of raw locks when possible. (5) Consider lock-free algorithms using CAS
operations.

Page 18
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 4: JVM Internals

JVM questions show whether you understand what happens under the hood. This knowledge is
crucial for debugging performance issues in production.

4.1 JVM Architecture - How Java Code Actually Runs


When you run a Java program, here is what happens:

• Step 1: The Java compiler (javac) compiles .java files into .class files containing bytecode.
• Step 2: The ClassLoader loads .class files into JVM memory.
• Step 3: The Bytecode Verifier checks the bytecode for safety and correctness.
• Step 4: The Execution Engine runs the bytecode. It has two parts: the Interpreter (executes
bytecode line by line) and the JIT Compiler (compiles hot code paths to native machine code for
speed).

TIP: The JIT compiler is why Java is fast despite being 'interpreted'. Frequently executed code
(hot spots) gets compiled to native code, which runs as fast as C++. This is also where the
name 'HotSpot JVM' comes from.

4.2 Memory Model - Heap, Stack, Metaspace


Heap
Where all objects live. Shared across all threads. Divided into:

• Young Generation: Where new objects are created. Has Eden space and two Survivor spaces
(S0, S1). Minor GC happens here - it is fast because most objects die young.
• Old Generation (Tenured): Objects that survive multiple Minor GCs get promoted here. Major
GC (or Full GC) cleans this space - it is slower and can cause pauses.

Stack
Each thread has its own stack. Stores local variables, method calls, and partial results. When a
method is called, a new frame is pushed onto the stack. When it returns, the frame is popped. Stack
memory is automatically managed - no GC needed. StackOverflowError occurs when the stack is full
(usually due to infinite recursion).

Metaspace (replaced PermGen in Java 8)


Stores class metadata, method information, and static variables. Unlike PermGen which had a fixed
size, Metaspace grows dynamically from native memory. This eliminated the common PermGen

Page 19
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

OutOfMemoryError. You can still limit it with -XX:MaxMetaspaceSize.

[Medium] Q: What is the difference between Stack and Heap memory?


A: Stack: thread-private, stores primitives and references, LIFO order, very fast allocation, fixed size
(configurable with -Xss), automatically cleaned when method returns. Heap: shared across threads,
stores objects, managed by Garbage Collector, larger and configurable (-Xms and -Xmx), slower
allocation. Key insight: a reference variable lives on the stack, but the object it points to lives on the
heap.

4.3 Garbage Collection - Types and Tuning


GC is critical for production performance. Understanding it can save you from serious outages.

How GC Works
The GC identifies objects that are no longer reachable from any GC Root (local variables, active
threads, static variables, JNI references). Unreachable objects are eligible for collection. The GC uses
'mark-and-sweep': first mark all reachable objects, then sweep (delete) unreachable ones.

GC Algorithms
• Serial GC (-XX:+UseSerialGC): Single-threaded. Good for small applications. Pauses all
threads.
• Parallel GC (-XX:+UseParallelGC): Multi-threaded. Default in Java 8. Good throughput but
longer pauses.
• G1 GC (-XX:+UseG1GC): Default since Java 9. Divides heap into regions. Balances
throughput and latency. Best for most applications.
• ZGC (-XX:+UseZGC): Ultra-low latency (sub-millisecond pauses). Good for large heaps.
Production-ready since Java 15.
• Shenandoah: Similar to ZGC. Low-pause GC. Available in OpenJDK.

[Hard] Q: Your production app has long GC pauses. How would you diagnose and fix it?
A: Step 1: Enable GC logging (-Xlog:gc* in Java 11+). Analyze the logs to see which GC type is
causing pauses and how often. Step 2: Check if heap is properly sized (-Xmx). Too small = frequent
GCs. Too large = longer pauses. Step 3: Look at promotion rate - if too many objects are being
promoted to Old Gen, increase Young Gen (-Xmn) or tune Survivor ratio. Step 4: Consider switching
to G1 or ZGC for lower pauses. Step 5: Check for memory leaks using heap dumps (jmap or
-XX:+HeapDumpOnOutOfMemoryError). Common causes: unbounded caches, listeners not being
unregistered, large collections growing indefinitely.

4.4 ClassLoading Mechanism


ClassLoading follows the Delegation Model. When a class needs to be loaded, the request goes UP
the hierarchy first:

Page 20
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

• Bootstrap ClassLoader: Loads core Java classes ([Link].*) from [Link]. Written in native
code.
• Platform/Extension ClassLoader: Loads extension classes from jre/lib/ext.
• Application ClassLoader: Loads classes from your application's classpath.
A ClassLoader first delegates to its parent. Only if the parent cannot find the class, the child tries to
load it. This prevents duplicate loading and ensures core classes cannot be overridden by application
code (security feature).

[Medium] Q: What is a ClassNotFoundException vs NoClassDefFoundError?


A: ClassNotFoundException is a checked exception thrown when you try to load a class dynamically
([Link](), loadClass()) and it is not found on the classpath. NoClassDefFoundError is an
Error thrown when the class was available at compile time but missing at runtime, or when static
initialization of the class failed. Key difference: ClassNotFoundException = dynamic loading failed.
NoClassDefFoundError = class was expected but disappeared or failed to initialize.

4.5 JVM Tuning Flags You Should Know


Flag Purpose Example

-Xms Initial heap size -Xms512m

-Xmx Maximum heap size -Xmx4g

-Xss Thread stack size -Xss512k

-XX:+UseG1GC Use G1 Garbage Collector

-XX:MaxGCPauseMillis Target max GC pause time 200

-XX:+HeapDumpOnOOME Heap dump on OOM Error

-Xlog:gc* Enable GC logging (Java 11+)

-XX:MaxMetaspaceSize Limit Metaspace size 256m

Page 21
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 5: Java 8 to 21 - Modern Java


Features

Modern Java features are increasingly asked in interviews. Companies want to know you are keeping
up with the language. Java 8 is the bare minimum - features from 9-21 set you apart.

5.1 Functional Interfaces and Lambda Expressions


A functional interface has exactly one abstract method. Lambdas are a short way to implement them
without creating a full class.

Built-in Functional Interfaces


• Predicate<T>: Takes T, returns boolean. Used for filtering. Example: [Link]().filter(s ->
[Link]() > 3)
• Function<T, R>: Takes T, returns R. Used for transformation. Example:
[Link]().map(String::toUpperCase)
• Consumer<T>: Takes T, returns nothing. Used for side effects. Example:
[Link]([Link]::println)
• Supplier<T>: Takes nothing, returns T. Used for lazy generation. Example: () -> new
ArrayList<>()

[Easy] Q: What is the difference between Predicate and Function?


A: Predicate takes an input and returns a boolean - it tests a condition. Function takes an input and
returns any output type - it transforms data. Predicate is a specialized Function where the return type
is always boolean. You could technically use Function<T, Boolean> instead of Predicate<T>, but
Predicate provides convenient methods like and(), or(), negate() for combining conditions.

5.2 Stream API - Operations, Collectors, Parallel Streams


Streams let you process collections in a functional, declarative way. They are lazy, meaning
intermediate operations do not execute until a terminal operation is called.

Key Concepts
• Intermediate operations (lazy): filter, map, flatMap, sorted, distinct, peek, limit, skip
• Terminal operations (trigger execution): collect, forEach, reduce, count, findFirst, anyMatch,
toList
// Find top 3 highest-paid employees in Engineering List<String> topPaid =
[Link]() .filter(e -> [Link]().equals("Engineering"))

Page 22
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

.sorted([Link](Employee::getSalary) .reversed()) .limit(3)


.map(Employee::getName) .collect([Link]()); // Group employees by
department Map<String, List<Employee>> byDept = [Link]()
.collect([Link](Employee::getDept)); // Average salary per department
Map<String, Double> avgSalary = [Link]() .collect([Link](
Employee::getDept, [Link](Employee::getSalary)));

[Medium] Q: What is the difference between map() and flatMap()?


A: map() transforms each element into exactly one element (one-to-one). flatMap() transforms each
element into zero or more elements and flattens the result (one-to-many). Example: if you have a List
of sentences and want all words, map(s -> [Link](" ")) gives you a Stream of String arrays. flatMap(s
-> [Link]([Link](" "))) gives you a flat Stream of individual words. flatMap 'unwraps' the
nested structure.

[Hard] Q: When should you use parallel streams? What are the pitfalls?
A: Use parallel streams when: (1) you have a large dataset (10,000+ elements), (2) the operation is
CPU-intensive and stateless, (3) the data source splits well (ArrayList is great, LinkedList is terrible).
Pitfalls: (1) Shared mutable state causes race conditions. (2) The common ForkJoinPool is shared
across your application - a slow parallel stream can starve other tasks. (3) I/O operations in parallel
streams are problematic because threads block. (4) Order-dependent operations (findFirst, limit) lose
performance benefits. In practice, parallel streams should be used sparingly and always
benchmarked.

5.3 Optional - The Right Way to Use It


Optional was created to make null handling explicit and avoid NullPointerException. But it is
commonly misused.

// GOOD usage Optional<User> user = [Link](id); String name =


[Link](User::getName).orElse("Unknown"); // Chain operations safely String city =
user .map(User::getAddress) .map(Address::getCity) .orElse("N/A"); // Throw if
absent User u = [Link](() -> new UserNotFoundException("User " + id + "
not found"));

COMMON MISTAKE: Common misuses of Optional: (1) Never use Optional as a method
parameter - it adds unnecessary wrapping. (2) Never use Optional for class fields - use null
instead. (3) Never call get() without checking isPresent() - use orElse(), orElseThrow(), or map()
instead. (4) [Link](null) throws NPE - use [Link]() when the value might be
null.

5.4 Records, Sealed Classes, Pattern Matching


Records (Java 14+)
Records are immutable data carriers. They automatically generate constructor, getters, equals(),
hashCode(), and toString(). Perfect for DTOs, value objects, and data transfer.

Page 23
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

// Instead of 50 lines of boilerplate: public record Employee(String name, int age,


String dept) {} // You get: constructor, name(), age(), dept(), // equals(),
hashCode(), toString() - all for free!

Sealed Classes (Java 17+)


Sealed classes restrict which classes can extend them. This gives you exhaustive pattern matching
and better domain modeling.

public sealed interface Shape permits Circle, Rectangle, Triangle {} public record
Circle(double radius) implements Shape {} public record Rectangle(double w, double
h) implements Shape {} public record Triangle(double a, double b, double c)
implements Shape {}

Virtual Threads (Java 21 - Project Loom)


Virtual Threads are lightweight threads managed by the JVM, not the OS. You can create millions of
them without running out of memory. This is a game-changer for I/O-bound applications like web
servers.

// Create a million virtual threads - no problem! try (var executor =


[Link]()) { for (int i = 0; i < 1_000_000; i++) {
[Link](() -> { // Each task gets its own virtual thread return
callExternalAPI(); }); } }

[Medium] Q: How do Virtual Threads differ from Platform Threads?


A: Platform threads are 1:1 mapped to OS threads - each costs about 1MB of stack memory, and
you can typically run a few thousand. Virtual threads are managed by the JVM and
mounted/unmounted on platform (carrier) threads as needed - they cost about 1KB each, and you
can run millions. When a virtual thread blocks (on I/O, sleep, lock), it is unmounted from its carrier
thread, freeing it for other virtual threads. This makes the thread-per-request model viable again for
high-throughput servers. However, virtual threads do not help with CPU-bound tasks - they shine
with I/O-bound workloads.

Page 24
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 6: Design Patterns & Best Practices

Interviewers ask about patterns to check if you can write maintainable, extensible code. You do not
need to know all 23 GoF patterns - these are the ones that actually come up.

6.1 Singleton - Why Interviewers Still Ask This


The Singleton pattern ensures only one instance of a class exists. It sounds simple but has many
pitfalls that interviewers love to explore.

The Best Way: Enum Singleton


public enum DatabaseConnection { INSTANCE; private final Connection connection;
DatabaseConnection() { [Link] = createConnection(); } public Connection
getConnection() { return connection; } }

Why Enum? It is thread-safe by default, prevents reflection attacks (you cannot create enum instances
via reflection), handles serialization correctly (always returns the same instance), and is the simplest
implementation. Joshua Bloch (author of Effective Java) calls it the best way to implement Singleton.

Double-Checked Locking (Classic Interview Answer)


public class Singleton { private static volatile Singleton instance; // volatile!
private Singleton() {} public static Singleton getInstance() { if (instance == null)
{ // First check (no lock) synchronized ([Link]) { if (instance == null) {
// Second check (with lock) instance = new Singleton(); } } } return instance; } }

[Hard] Q: Why is the volatile keyword necessary in double-checked locking?


A: Without volatile, the JVM can reorder instructions. The line 'instance = new Singleton()' involves:
(1) allocate memory, (2) call constructor, (3) assign reference. The JVM might reorder it to (1)
allocate, (3) assign, (2) construct. If Thread A is between steps 3 and 2, Thread B sees a non-null
instance but the object is not fully constructed yet. volatile prevents this reordering by establishing a
happens-before relationship. This is a subtle but critical concurrency issue.

6.2 SOLID Principles with Real Examples


S - Single Responsibility Principle
A class should have only one reason to change. If your UserService handles authentication, email
sending, and database operations, it has too many responsibilities. Split into AuthService,
EmailService, and UserRepository.

O - Open/Closed Principle

Page 25
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Classes should be open for extension but closed for modification. Use interfaces and polymorphism.
Instead of if-else chains for different payment types, create a PaymentProcessor interface with
CreditCardProcessor, UPIProcessor, etc.

L - Liskov Substitution Principle


Subtypes must be substitutable for their base types without breaking the program. Classic violation:
Square extending Rectangle. If setWidth() changes the height in Square but not in Rectangle, code
that depends on Rectangle behavior breaks.

I - Interface Segregation Principle


Clients should not be forced to depend on interfaces they do not use. Do not create one fat interface
with 20 methods. Split into smaller, focused interfaces. A Printer interface should not force
implementing a scan() method.

D - Dependency Inversion Principle


High-level modules should depend on abstractions, not concrete implementations. Your OrderService
should depend on a PaymentGateway interface, not directly on StripePayment. This makes it easy to
swap implementations and write tests.

[Medium] Q: Give a real-world example of violating and fixing the Open/Closed Principle.
A: Violation: A NotificationService with if-else for each channel: if (type == EMAIL) sendEmail() else if
(type == SMS) sendSMS(). Adding push notifications requires modifying this class. Fix: Create a
Notifier interface with send() method. Implement EmailNotifier, SmsNotifier, PushNotifier. The
NotificationService takes a List of Notifiers and calls send() on each. Adding a new channel means
adding a new class - no modification to existing code.

Page 26
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Chapter 7: Tricky Interview Questions &


Gotchas

These are questions designed to test your depth. Many senior developers get these wrong. Go
through each one carefully.

7.1 Output-Based Questions


[Medium] Q: What is the output?
String s1 = "Hello"; String s2 = "Hello"; String s3 = new String("Hello");
[Link](s1 == s2); // ? [Link](s1 == s3); // ?
[Link]([Link](s3)); // ?

A: Output: true, false, true. s1 and s2 both point to the same object in the String Pool, so == returns
true. s3 is created with 'new', so it is a different object on the heap - == returns false. equals()
compares content, which is the same - returns true.

[Medium] Q: What is the output?


Integer a = 127; Integer b = 127; Integer c = 128; Integer d = 128;
[Link](a == b); // ? [Link](c == d); // ?

A: Output: true, false. Java caches Integer values from -128 to 127 (IntegerCache). So a and b point
to the same cached object. But 128 is outside the cache range, so c and d are different objects. This
is why you should always use .equals() for wrapper comparisons, not ==. This catches many
experienced developers off guard.

[Hard] Q: What is the output?


try { return 1; } finally { return 2; }

A: Output: 2. The finally block always executes, even after a return statement. The return in finally
overrides the return in try. This is why you should NEVER put a return statement in a finally block - it
swallows exceptions and overrides return values. Most linters and IDEs will warn you about this.

7.2 Find the Bug Questions


[Medium] Q: What is wrong with this code?
Map<Employee, String> map = new HashMap<>(); Employee emp = new Employee("John",
101); [Link](emp, "Engineering"); [Link]("Jane"); // Modify the key!
[Link]([Link](emp)); // What prints?

Page 27
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

A: Output: null! When emp was put in the map, its hashCode was calculated based on 'John'. After
changing the name to 'Jane', the hashCode changes. Now get(emp) looks in a different bucket than
where the entry was stored. The original entry becomes unreachable - this is a memory leak! Lesson:
HashMap keys should be immutable. This is why String and Integer are preferred as map keys.

7.3 Scenario-Based Questions


[Hard] Q: You have a method that reads from a file and writes to a database. How would
you handle exceptions properly?
A: Use try-with-resources for the file. Wrap the database operation in a separate try-catch. Key
decisions: (1) Should the database write fail if the file read fails? (2) Should you retry? (3) What
transaction guarantees do you need? For the file, use BufferedReader in try-with-resources. For the
database, use a transaction - if anything fails, rollback. Log the original exception with full stack trace.
Wrap low-level exceptions in meaningful business exceptions (throw new
DataImportException('Failed to import user data', cause)). Consider using a retry library like
Resilience4j for transient failures.

[Hard] Q: Your application is using 90% of available memory. How do you diagnose it?
A: Step 1: Take a heap dump using jmap -dump:format=b,file=[Link] PID. Step 2: Analyze with
Eclipse MAT or VisualVM. Look for dominator tree - which objects hold the most memory. Step 3:
Check for common memory leak patterns: static collections that keep growing, listeners not removed,
unclosed resources (connections, streams), ThreadLocal variables not cleaned up, large caches
without eviction. Step 4: Enable GC logging to see if the heap is growing over time (true leak) or just
large at steady state. Step 5: If it is a leak, find the allocation path in MAT and fix the root cause.

Page 28
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Bonus: Cheat Sheets & Quick Revision

Print these out or save them on your phone. Go through them 30 minutes before your interview.

Collections Cheat Sheet


Collection Ordered? Sorted? Duplicates? Thread-safe? Null?

ArrayList Yes No Yes No Yes

LinkedList Yes No Yes No Yes

HashSet No No No No 1 null

LinkedHashSet Insertion No No No 1 null

TreeSet Yes Yes No No No

HashMap No No Keys: No No 1 null key

LinkedHashMap Insertion No Keys: No No 1 null key

TreeMap Yes Yes Keys: No No No null key

ConcurrentHashMap No No Keys: No Yes No nulls

CopyOnWriteArrayList Yes No Yes Yes Yes

Top 30 One-Liner Interview Answers


1. Why is Java platform independent?
Bytecode runs on any JVM, regardless of the underlying OS.

2. What is JIT compiler?


Compiles hot bytecode to native machine code at runtime for speed.

3. Difference between JDK, JRE, JVM?


JDK = development tools + JRE. JRE = runtime + JVM. JVM = executes bytecode.

4. What is autoboxing?
Automatic conversion between primitives and their wrapper classes (int to Integer).

5. What is method overloading?


Same method name, different parameters. Resolved at compile time.

6. What is method overriding?


Child class redefines parent's method. Resolved at runtime.

7. Can we override static methods?


No. Static methods are bound to the class, not the object. It is method hiding.

8. What is the final keyword?


final variable = constant. final method = cannot override. final class = cannot extend.

Page 29
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

9. What is a static block?


Executes once when the class is loaded. Used for static initialization.

10. What is the this keyword?


Reference to the current object instance.

11. What is super keyword?


Reference to the parent class. Used to call parent constructor or methods.

12. What is type erasure?


Generics are removed at compile time. At runtime, List<String> is just List.

13. What is a marker interface?


Interface with no methods (Serializable, Cloneable). Acts as a metadata flag.

14. Comparable vs Comparator?


Comparable: natural ordering, class implements it. Comparator: custom ordering, separate class.

15. What is the transient keyword?


Fields marked transient are excluded from serialization.

16. What is a WeakReference?


Reference that does not prevent garbage collection. Used in caches.

17. What is the diamond operator?


Type inference: Map<String, List<Integer>> m = new HashMap<>();

18. What is [Link]()?


Moves a String to the String Pool and returns the pool reference.

19. What is a functional interface?


Interface with exactly one abstract method. Can be used with lambdas.

20. What are default methods?


Interface methods with a body (Java 8+). Allow adding methods without breaking implementations.

21. What is the Stream API?


Declarative way to process collections using functional operations like filter, map, reduce.

22. What is a ConcurrentModificationException?


Thrown when a collection is modified while being iterated with a fail-fast iterator.

23. What is the volatile keyword?


Ensures variable reads and writes go to main memory, providing visibility guarantee.

24. What is a daemon thread?


Background thread that does not prevent JVM from exiting. Example: Garbage Collector.

25. What is ThreadLocal?


Gives each thread its own copy of a variable. No synchronization needed.

26. What is the Fork/Join framework?


Divides tasks into subtasks recursively, processes in parallel, then joins results.

27. What is CompletableFuture?


Async computation that can be chained, combined, and composed with callbacks.

28. What is a record in Java?


Immutable data carrier with auto-generated constructor, getters, equals, hashCode, toString.

29. What are sealed classes?

Page 30
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

Classes that restrict which other classes can extend them. Enables exhaustive pattern matching.

30. What are Virtual Threads?


Lightweight JVM-managed threads (Java 21). Can create millions for I/O-bound tasks.

Page 31
Java Core & Advanced - Interview Mastery Guide by Aman Mishra

You Made It!


If you have read this entire guide and understood the concepts (not just memorized
them), you are already better prepared than most candidates. Remember:

✓ Understand the WHY, not just the WHAT

✓ Practice explaining concepts out loud

✓ Build something small with each concept you learn

✓ It is okay to say 'I do not know' in an interview - then explain how you would find out

✓ Confidence comes from understanding, not memorization

Connect with me for more backend content:

LinkedIn: Aman Mishra | Topmate: Check my profile for more guides

If this guide helped you, share it with someone preparing for interviews.
Good luck! You have got this.

© 2025 Aman Mishra. All rights reserved.

Page 32

You might also like