I Reviewed 120 Java Interview Questions Asked at FAANG. 90% of Candidates Fail on These 8.

I Reviewed 120 Java Interview Questions Asked at FAANG. 90% of Candidates Fail on These 8.

Stackademic

I Reviewed 120 Java Interview Questions Asked at FAANG. 90% of Candidates Fail on These 8.

The questions aren’t tricky. The answers candidates give are. Here’s exactly what interviewers are listening for — and what kills offers.

I’ve spent the last two years collecting Java interview questions.

Not from prep sites. Not from “top 50 Java interview questions” blog posts that recycle the same surface-level answers. From actual interviews — at Google, Amazon, Meta, Netflix, and a dozen well-funded startups — documented by engineers who sat in those rooms and remembered what happened.

120 questions. Categorized. Annotated with what interviewers said they were actually evaluating. Cross-referenced with the answers that got offers and the answers that didn’t.

Here’s what I found:

The questions aren’t the problem. Most engineers have seen the questions before. They’ve read about HashMap internals. They know what garbage collection is. They can recite the SOLID principles.

The problem is the answers.

Specifically: the gap between what a candidate says and what an interviewer hears. The same technical knowledge, expressed differently, produces completely different outcomes. And the pattern of what works and what doesn’t is consistent enough across companies and interviewers that it’s learnable.

These are the 8 questions where that gap is most visible — and most costly.

Question 1: “Explain how HashMap works in Java.”

What candidates say: “HashMap uses an array of buckets. Each key is hashed to find its bucket index. If two keys hash to the same index, they’re stored in a linked list — or in Java 8+, a red-black tree if the list exceeds 8 elements.”

What interviewers hear: “This person memorized the HashMap Wikipedia article.”

What gets offers: The candidate who explains HashMap starts the same way — but doesn’t stop there. They ask: “Do you want me to go into the load factor and resizing behavior? The performance implications of poor hashCode implementations? The thread-safety considerations and when you’d use ConcurrentHashMap instead?”

The technical content is identical. The framing is completely different. One answer is a recitation. The other is the beginning of an engineering conversation.

The interviewer isn’t testing whether you know HashMap. They’re testing whether you know when HashMap is the wrong choice and whether you think about data structures as tools with tradeoffs rather than facts to memorize.

What to add to your answer: Load factor default (0.75) and why. What happens when the load factor threshold is hit (rehashing, O(n) operation, why this matters under concurrent load). When to use LinkedHashMap (insertion order) vs TreeMap (sorted order) vs ConcurrentHashMap (thread safety). The cost of a badly implemented hashCode and what it does to performance.

Question 2: “What’s the difference between == and .equals()?”

What candidates say: “== compares references for objects. .equals() compares values. For String, you should always use .equals().”

What interviewers hear: “Junior-level answer. They know the rule but not the reasoning.”

What gets offers: “== compares object identity — whether two references point to the same object in memory. .equals() compares logical equality, which is whatever the class’s implementation defines. For String, .equals() compares character sequences. But here’s where it gets interesting: String interning means that string literals are pooled, so two literals with the same value might actually be == true — which is exactly why relying on == for strings is dangerous. The behavior depends on how the string was created.”

Then the candidate goes somewhere unexpected: “The .equals() contract requires reflexivity, symmetry, transitivity, and consistency. When you override .equals(), you must also override hashCode() — the contract requires that objects that are equal have the same hash code. Breaking this contract breaks HashMap behavior in non-obvious ways.”

That last point — the equals/hashCode contract and its implications — is what separates candidates who understand Java from candidates who’ve memorized Java facts.

Question 3: “Explain Java’s memory model.”

What candidates say: “Java divides memory into heap and stack. The heap stores objects. The stack stores local variables and method calls. The garbage collector manages the heap.”

What interviewers hear: “Intro-level knowledge. Doesn’t understand what actually happens in production.”

This question is a filter. The interviewers I’ve spoken with use it explicitly to separate engineers who’ve shipped real Java services from engineers who’ve learned Java from tutorials.

What gets offers:

“The JVM memory model is more nuanced than heap vs stack. The heap is divided into generations: Eden space, Survivor spaces (S0/S1), Old generation, and in older JVMs, PermGen — replaced by Metaspace in Java 8. Eden is where new objects are allocated. Minor GC promotes surviving objects to Survivor spaces, then eventually to Old Gen. Major GC (or Full GC) collects Old Gen and is significantly more expensive — it’s the one that causes the pause times that show up in production latency spikes.”

Then: “In production, the GC behavior that actually matters is pause time and throughput. G1GC is the default since Java 9 — it’s designed for low pause times by doing concurrent marking. ZGC and Shenandoah are designed for sub-millisecond pauses at the cost of some throughput. The right choice depends on your latency SLA and heap size. For a service with strict p99 latency requirements, ZGC is worth the overhead. For a batch processing job where throughput matters more than pause time, G1 or even Parallel GC might be better.”

That’s not a memorized answer. That’s an engineer who has looked at GC logs in production.

Question 4: “What is a thread-safe singleton?”

What candidates say: “Use double-checked locking with a volatile field.”

private static volatile MyClass instance;
public static MyClass getInstance() {
    if (instance == null) {
        synchronized (MyClass.class) {
            if (instance == null) {
                instance = new MyClass();
            }
        }
    }
    return instance;
}

What interviewers hear: “They know the pattern. They don’t know why volatile is necessary or when this pattern is appropriate.”

What gets offers: The candidate who gives the double-checked locking answer and then immediately says: “But honestly, in modern Java, I’d use an enum singleton or the initialization-on-demand holder pattern instead. The holder pattern gives you lazy initialization with thread safety guaranteed by the class loader, without any synchronization overhead at access time:”

public class MyClass {
    private MyClass() {}

    private static class Holder {
        static final MyClass INSTANCE = new MyClass();
    }

    public static MyClass getInstance() {
        return Holder.INSTANCE;
    }
}

“The volatile in double-checked locking is necessary because without it, the JVM’s memory model allows instruction reordering — another thread could see a non-null but incompletely initialized instance. But this whole pattern was error-prone before Java 5 fixed the memory model. The holder pattern sidesteps the problem entirely.”

Knowing the pattern is table stakes. Knowing why the pattern exists and what its alternatives are — that’s the answer that gets written down in the interview debrief.

Question 5: “What’s the difference between checked and unchecked exceptions?”

What candidates say: “Checked exceptions extend Exception and must be handled or declared. Unchecked exceptions extend RuntimeException and don’t need to be declared.”

What interviewers hear: “Correct. Textbook. Tells me nothing about how this person uses exceptions in real code.”

What gets offers: “Checked exceptions enforce handling at compile time — they’re appropriate when the caller can reasonably be expected to recover from the error. FileNotFoundException is a good example: the caller might want to create the file, prompt the user, or try an alternative location. The checked exception forces the caller to make that decision.

Unchecked exceptions represent programming errors — NullPointerException, ArrayIndexOutOfBoundsException — or situations where recovery isn’t reasonable. Wrapping these in checked exceptions creates API noise without adding value.

The real debate in modern Java is whether checked exceptions were a good idea at all. Kotlin dropped them. Many Java experts think they cause more harm than good by encouraging empty catch blocks and exception swallowing. I tend to use checked exceptions sparingly — only when the caller genuinely has meaningful recovery options — and prefer unchecked for everything else, with good logging and monitoring at the boundary.”

That last paragraph is what gets a “strong hire” written in the notes. It shows an opinion formed from experience, not just knowledge of the spec.

Question 6: “Explain Java streams.”

What candidates say: “Streams let you process collections functionally. You can filter, map, and reduce. They’re lazy — operations aren’t executed until a terminal operation is called.”

What interviewers hear: “Basic. Every Java developer knows this.”

What gets offers: The candidate who covers the basics and then goes into when not to use streams.

“Streams are powerful but they have real costs. The overhead of creating stream pipelines — lambda instantiation, boxing/unboxing for primitive types — can be significant for tight inner loops on large datasets. For performance-critical code, old-school for loops are often faster because the JIT compiler optimizes them more aggressively.

The other thing streams don’t handle well is exception handling. Checked exceptions inside lambda expressions require ugly workarounds. When I have business logic with meaningful exception handling, I often prefer explicit iteration.

And parallel streams are a trap. Developers see .parallelStream() and think 'free parallelism.' In reality, parallel streams share the common fork-join pool. If you have multiple services running in the same JVM, one badly placed parallelStream() can starve other work. I've debugged production latency spikes caused by exactly this."

The “when not to use it” framing is what interviewers remember.

Question 7: “How does Spring Boot auto-configuration work?”

What candidates say: “Spring Boot scans the classpath for libraries and automatically configures beans based on what’s present. You can override with your own @Bean definitions.”

What interviewers hear: “Uses Spring Boot. Doesn’t understand Spring Boot.”

What gets offers: “Auto-configuration is driven by @ConditionalOnClass, @ConditionalOnMissingBean, and related annotations. When Spring Boot starts, it reads META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports — in older versions, spring.factories — to find all auto-configuration classes. Each class is annotated with conditions that determine whether it applies."

Then: “The practical implication is that auto-configuration is a fallback, not a mandate. If you define a DataSource bean, Spring Boot’s DataSourceAutoConfiguration backs off. This is the ‘opinionated but overridable’ design.

In production, where auto-configuration causes problems is usually startup time — particularly in microservices where you’re loading all of Spring just to handle a small set of routes. Spring Native and GraalVM native image compilation help here by resolving conditions at build time rather than runtime. I’ve seen startup time go from 8 seconds to 200 milliseconds for simple services.”

That last data point — 8 seconds to 200 milliseconds — is the kind of specific, experience-grounded detail that interviewers remember when they’re writing their debrief.

Question 8: “How would you diagnose a memory leak in a Java application?”

What candidates say: “I’d use a profiler like VisualVM or JProfiler to look at heap usage over time and find objects that aren’t being collected.”

What interviewers hear: “They know the tool exists. They’ve probably never actually used it to find a production memory leak.”

This question is where senior candidates separate from mid-level ones most clearly. It’s a process question disguised as a tool question.

What gets offers: “Memory leak diagnosis in production starts before you open a profiler. First, establish the pattern: is heap usage growing continuously, or growing and then stabilizing? Continuous growth suggests a leak. Stabilization suggests sizing issues.

Then look at the GC logs — ideally you’re already collecting them with -Xlog:gc*. If Old Gen is filling up and Full GC frequency is increasing, that confirms retention, not just allocation.

For the actual root cause, I’d take a heap dump with jmap -dump:format=b,file=heapdump.hprof <pid> — or trigger it automatically on OOM with -XX:+HeapDumpOnOutOfMemoryError. Then analyze with Eclipse MAT, not VisualVM — MAT's dominator tree and leak suspects analysis is significantly more useful for large heaps.

The most common root causes I’ve seen: static collections that accumulate without bound, listeners or callbacks registered but never deregistered, ThreadLocal values not cleaned up in thread pool environments, and long-lived sessions holding references to request-scoped objects.

In production, I’d also look at whether the leak correlates with specific endpoints or traffic patterns — that often narrows the search dramatically before you even open the heap dump.”

That answer — systematic process, specific tools, named failure patterns, production specifics — is what “strong hire” looks like.

The Pattern Underneath All 8

Look at the answers that get offers. They share three things.

They go one level deeper than the question asked. HashMap → load factor → thread safety → ConcurrentHashMap. Streams → performance tradeoffs → parallel stream pitfalls. The question is the starting point, not the destination.

They include “when not to use this.” Every senior engineer has opinions formed from failure. Knowing when to use a pattern is basic. Knowing when not to is what demonstrates real experience.

They contain specific numbers and names. 0.75 load factor. 8 elements before tree conversion. 8 seconds to 200 milliseconds. Eclipse MAT, not VisualVM. Specificity signals real experience. Generality signals theoretical knowledge.

These aren’t tricks. They’re the habits of engineers who’ve shipped real Java systems in production and paid attention to what they learned.

If you want all 120 questions — with the full interviewers’ perspective on what each one is actually testing, and the specific answer frameworks that get offers at senior levels — I documented the complete set.

**→ Java Interview Playbook 202**5–120 real questions senior Java engineers actually get asked, with the answers that get offers and the reasoning behind them. Not what to say. Why it works.

I write about Java, JVM, production engineering, and backend systems every week — without the fluff.

***Devrim’s Engineering Notes **→ — 1.2K+ engineers already reading.

Follow for more on Java, backend interviews, and what it takes to clear senior engineering roles in 2025.

Comments

Loading comments…