> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Java Interview Questions

> Complete interview guide for Java developers covering Core Java, OOP, Collections, Multithreading, Spring, memory management, and modern Java features -- with JVM internals and production-level trade-off analysis

Java remains one of the most interviewed languages in the industry, powering backend systems at companies from startups to Fortune 500 enterprises. Java interviews are distinctive because they test not just language syntax, but your understanding of the JVM, memory model, concurrency primitives, and the Spring ecosystem -- topics that separate developers who write Java from engineers who build reliable Java systems. The questions below are organized from fundamentals through advanced topics, covering the full range of what you will encounter in Java-focused technical interviews.

For each section, the questions progress in depth. If you can answer the earlier questions confidently, you are at a solid junior/mid level. If you can articulate the nuances in the later questions with trade-off analysis, you are demonstrating senior-level understanding.

<hr />

## 1. Java Fundamentals

<AccordionGroup>
  <Accordion title="What is Java?">
    Java is an object-oriented, platform-independent programming language that compiles to bytecode and runs on the Java Virtual Machine (JVM). It follows the WORA principle — Write Once, Run Anywhere.

    **But here is what a strong candidate actually says:**

    * Java is statically typed, class-based, and compiled to an intermediate bytecode format (`.class` files) rather than native machine code. This bytecode runs on the JVM, which provides platform independence, automatic memory management via garbage collection, and a security sandbox.
    * The "platform independence" story has nuance: your code is portable, but your JVM is not. Each OS needs its own JVM implementation (HotSpot, OpenJ9, GraalVM). In practice, you still hit platform-specific issues — file path separators, line endings, native library loading via JNI, and even thread scheduling behavior differs across OS implementations.
    * Java's real staying power comes from its ecosystem maturity: the Collections Framework, `java.util.concurrent`, Spring, Hibernate, and build tools like Maven/Gradle. Companies like Netflix, LinkedIn, and Uber run massive backend services on Java because of its predictable performance profile at scale and battle-tested libraries.
    * Since Java 9, the language has adopted a six-month release cadence. Modern Java (17+) includes records, sealed classes, pattern matching, text blocks, and virtual threads (Project Loom in Java 21) — it is a fundamentally different language than Java 8.

    **What interviewers are really testing:** Whether you understand Java beyond the textbook definition — do you know why companies actually choose Java over alternatives, and are you aware of modern Java evolution?

    **Red flag answer:** "Java is an OOP language that is platform independent." Full stop. No mention of JVM, bytecode, or any real-world context.

    **Follow-up:**

    * "What makes Java 'platform independent' and where does that abstraction break down in production?"
    * "How does Java compare to Kotlin or Go for backend services, and when would you pick one over the other?"
    * "What Java version features have you used in production, and which ones actually changed how you write code?"
  </Accordion>

  <Accordion title="Explain JVM, JRE, and JDK">
    * **JVM (Java Virtual Machine):** Executes Java bytecode. It is the runtime engine that provides memory management, garbage collection, JIT compilation, and a security model.
    * **JRE (Java Runtime Environment):** Contains the JVM plus core class libraries (`java.lang`, `java.util`, etc.) needed to run Java applications. No development tools included.
    * **JDK (Java Development Kit):** Includes the JRE plus development tools — `javac` (compiler), `jdb` (debugger), `javap` (disassembler), `jconsole`, `jvisualvm`, and more.

    **Deep dive a strong candidate adds:**

    * The JVM is not a single monolithic thing — it has subsystems: the **Class Loader** (loads `.class` files using bootstrap, extension, and application classloaders), the **Execution Engine** (interpreter + JIT compiler), and the **Runtime Data Areas** (heap, stack, method area, PC registers, native method stack).
    * **JIT (Just-In-Time) compilation** is where Java gets its performance. The JVM starts by interpreting bytecode, then identifies "hot" methods (called frequently) and compiles them to native machine code at runtime. HotSpot JVM uses C1 (client) and C2 (server) compilers. This is why Java apps have a "warm-up" period — first few thousand requests are slower.
    * Since Java 9, the JRE is no longer distributed separately. The `jlink` tool lets you create custom runtime images containing only the modules your app needs, reducing deployment footprint from \~200MB to sometimes under 30MB.
    * In containerized deployments (Docker/K8s), understanding JVM ergonomics matters: older JVMs did not respect container memory limits (`-XX:+UseContainerSupport` was added in Java 10). This caused OOM kills in production for many teams running Java 8 in Docker.

    **What interviewers are really testing:** Whether you understand the JVM as an execution platform — not just a definition, but classloading, JIT compilation, and real deployment implications.

    **Red flag answer:** Reciting "JVM runs code, JRE has libraries, JDK has tools" without any mention of JIT, classloading, or how these distinctions matter in practice.

    **Follow-up:**

    * "Walk me through what happens from when you type `java MyApp` to when `main()` starts executing."
    * "What is JIT compilation, and why do Java apps have a warm-up period?"
    * "How do you size JVM memory in a Docker container, and what goes wrong if you get it wrong?"
  </Accordion>

  <Accordion title="What are access modifiers in Java?">
    `public`, `protected`, `default` (package-private), and `private` — they control the visibility of classes, methods, and variables.

    **What a strong candidate explains:**

    | Modifier    | Same Class | Same Package | Subclass (diff pkg) | Everywhere |
    | ----------- | ---------- | ------------ | ------------------- | ---------- |
    | `private`   | Yes        | No           | No                  | No         |
    | `default`   | Yes        | Yes          | No                  | No         |
    | `protected` | Yes        | Yes          | Yes                 | No         |
    | `public`    | Yes        | Yes          | Yes                 | Yes        |

    * **`private`** is your first choice for fields — it enforces encapsulation. Expose behavior through methods, not raw state. In practice, this is the most important modifier for API design.
    * **`default` (package-private)** is underused but powerful. It is the right choice for implementation classes that should not be part of your public API. For example, if you have an internal `CacheEvictionStrategy` that callers should never directly reference, package-private keeps it hidden without extra ceremony.
    * **`protected`** is often misunderstood — it grants access to subclasses *and* everything in the same package. This is a wider scope than most developers expect. It couples your class hierarchy, so use it sparingly.
    * **`public`** is a commitment. Once a method is public, removing or changing it is a breaking change. In library design, you should default to the most restrictive modifier and only widen when necessary. Joshua Bloch's Effective Java calls this "minimize accessibility."
    * Top-level classes can only be `public` or `default` — not `private` or `protected`. Inner classes can use all four.

    **What interviewers are really testing:** Whether you think about encapsulation as a design tool, not just a keyword to memorize.

    **Red flag answer:** Listing the four modifiers without explaining when you would choose one over another, or not knowing that `default` means package-private (not `public`).

    **Follow-up:**

    * "If you are designing a library that other teams will depend on, how do you decide which classes and methods to make public?"
    * "What is the relationship between access modifiers and Java's module system (JPMS) introduced in Java 9?"
    * "Can you override a method with a more restrictive access modifier? Why or why not?"
  </Accordion>

  <Accordion title="Difference between stack and heap memory?">
    **Stack:** Stores local variables, method parameters, and method call frames. Each thread gets its own stack.
    **Heap:** Stores all objects created with `new`. Shared across all threads. Managed by the garbage collector.

    **What a strong candidate adds:**

    * **Stack memory** is LIFO (last-in, first-out), automatically allocated and deallocated as methods are called and return. It is extremely fast because allocation is just moving a pointer. Stack size is typically 512KB-1MB per thread (configurable via `-Xss`). If you have 500 threads, that is 500MB just in stack memory.
    * **Heap memory** is where all object instances live. It is divided into generations: **Young Generation** (Eden + Survivor spaces) for short-lived objects, and **Old Generation** (Tenured) for long-lived objects. Since Java 8, the **Metaspace** (formerly PermGen) stores class metadata and lives in native memory, not the heap.
    * **Stack overflow** (`StackOverflowError`) happens with deep or infinite recursion — each method call adds a frame. **Out of memory** (`OutOfMemoryError`) happens when the heap is exhausted and GC cannot free enough space.
    * **Escape analysis** is a JVM optimization where the JIT compiler determines that an object does not "escape" a method scope and can be allocated on the stack instead of the heap. This eliminates GC pressure for short-lived objects. For example, an `Iterator` created inside a loop that never leaves the method may be stack-allocated.
    * **Practical impact:** In a high-throughput system processing 50K requests/second, excessive object allocation on the heap triggers frequent GC pauses. Tools like `jstat`, `jmap`, and async-profiler help identify allocation hotspots. Companies like Twitter and Netflix have teams dedicated to JVM tuning.

    **What interviewers are really testing:** Whether you understand JVM memory architecture deeply enough to diagnose performance issues — not just the textbook stack-vs-heap distinction.

    **Red flag answer:** "Stack is for primitives, heap is for objects" — this is partially wrong (local object references are on the stack, but the objects they point to are on the heap) and shows no awareness of GC generations or real-world implications.

    **Follow-up:**

    * "What happens if you set `-Xms` and `-Xmx` to the same value? Why would you do that?"
    * "Explain Young Generation vs Old Generation. How does an object move between them?"
    * "How would you diagnose a Java application that is spending 30% of its time in GC pauses?"
  </Accordion>

  <Accordion title="What are wrapper classes?">
    Wrapper classes convert primitive types into objects: `int` to `Integer`, `double` to `Double`, `boolean` to `Boolean`, etc. They are necessary because Java generics and collections only work with objects, not primitives.

    **What a strong candidate explains:**

    * **Autoboxing/unboxing** (Java 5+) handles conversion automatically: `Integer x = 5;` boxes the `int`, and `int y = x;` unboxes the `Integer`. This is syntactic sugar — the compiler inserts `Integer.valueOf(5)` and `x.intValue()` calls.
    * **The `Integer` cache gotcha:** `Integer.valueOf()` caches values from -128 to 127. So `Integer a = 127; Integer b = 127; a == b` returns `true` (same cached object), but `Integer a = 128; Integer b = 128; a == b` returns `false` (different objects). This trips up developers in production and is a classic interview trap. Always use `.equals()` for wrapper comparisons.
    * **Performance impact:** Autoboxing creates objects on the heap. In a tight loop processing millions of values, `List<Integer>` creates millions of `Integer` objects with \~16 bytes overhead each, versus a primitive `int[]` with zero overhead. This is why libraries like Eclipse Collections, HPPC, and Trove provide primitive-specialized collections. Project Valhalla (value types) aims to fix this at the language level.
    * **Null danger:** Wrappers can be `null`, primitives cannot. Unboxing a `null` `Integer` throws `NullPointerException`. This is a common production bug: `Map<String, Integer> map = ...; int value = map.get("missing");` throws NPE because `get()` returns `null`, and unboxing `null` blows up.

    **What interviewers are really testing:** Whether you understand the performance and correctness traps that autoboxing introduces, not just what wrapper classes are.

    **Red flag answer:** "Wrapper classes wrap primitives so you can use them in collections" with no mention of autoboxing pitfalls, caching, or performance overhead.

    **Follow-up:**

    * "Why does `new Integer(5) == new Integer(5)` return `false` but `Integer.valueOf(5) == Integer.valueOf(5)` return `true`?"
    * "In a hot loop processing 10 million records, what is the performance difference between `List<Integer>` and `int[]`?"
    * "How does Project Valhalla aim to address the primitive/object divide?"
  </Accordion>
</AccordionGroup>

<hr />

## 2. Object-Oriented Programming

<AccordionGroup>
  <Accordion title="What are the main OOP principles?">
    The four pillars: **Encapsulation**, **Inheritance**, **Polymorphism**, and **Abstraction**.

    **What a strong candidate explains (with real-world framing):**

    * **Encapsulation:** Bundling data and methods together and restricting direct access to internal state. It is not just "use getters and setters" — it is about hiding *invariants*. For example, a `BankAccount` class should not expose `balance` directly because external code could set it to a negative number, violating business rules. The getter/setter pattern is a tool, not the goal — the goal is protecting invariants.
    * **Inheritance:** Creating new classes based on existing ones to promote code reuse. But experienced engineers know inheritance is the most abused OOP principle. The "Favor composition over inheritance" guideline (Effective Java, Item 18) exists because inheritance creates tight coupling. For example, extending `HashMap` to add logging seems easy until `HashMap` changes its internal implementation and your subclass breaks. Use composition: wrap a `HashMap` in your class and delegate.
    * **Polymorphism:** The ability to treat objects of different classes through the same interface. **Compile-time** (overloading: same name, different parameters) vs **runtime** (overriding: subclass redefines parent method, resolved via vtable dispatch). Runtime polymorphism is the foundation of the Strategy pattern, plugin architectures, and dependency injection frameworks like Spring.
    * **Abstraction:** Exposing essential features while hiding implementation complexity. Abstract classes and interfaces are the mechanisms. A `PaymentProcessor` interface with a `charge()` method lets your code work with Stripe, PayPal, or Square without knowing the implementation. This is the **Dependency Inversion Principle** in action.

    **What interviewers are really testing:** Whether you can explain OOP in terms of design decisions you have actually made, not just recite definitions. They want to hear trade-offs: "I used composition here instead of inheritance because..."

    **Red flag answer:** Textbook definitions with no mention of when inheritance goes wrong, no real examples, no trade-offs.

    **Follow-up:**

    * "Give me a real example where you chose composition over inheritance. What would have gone wrong with inheritance?"
    * "How does runtime polymorphism actually work at the JVM level? What is a vtable?"
    * "Which SOLID principle is most related to each OOP pillar?"
  </Accordion>

  <Accordion title="Difference between abstraction and encapsulation?">
    **Abstraction** hides implementation complexity (what an object does, not how). **Encapsulation** hides internal state and controls access (protecting data integrity).

    **What a strong candidate explains:**

    * Think of **abstraction** as the "outside view" and **encapsulation** as the "inside lock." When you use `List<String> list = new ArrayList<>()`, you are programming to the `List` abstraction — you do not care that `ArrayList` uses a resizable array internally. That is abstraction.
    * **Encapsulation** is the enforcement mechanism. Making fields `private` and providing controlled access ensures that the internal representation can change without breaking callers. For example, a `Temperature` class might store degrees internally in Celsius but expose `getFahrenheit()` — encapsulation lets you change the internal storage to Kelvin later without any caller knowing.
    * **The subtle difference:** Abstraction is about *design* (choosing the right interfaces and what to expose). Encapsulation is about *implementation* (how you protect the internals). You can have abstraction without encapsulation (a well-designed interface with public fields) and encapsulation without abstraction (private fields but no meaningful interface hierarchy).
    * In Spring, `@Service` and `@Repository` are abstraction — they define roles. The `private` fields inside those beans are encapsulation. Together they produce a system where you can swap a `JpaUserRepository` for a `MongoUserRepository` without changing service layer code.

    **What interviewers are really testing:** Whether you understand these as distinct concepts or just conflate them into "hiding stuff."

    **Red flag answer:** "They are basically the same thing — hiding implementation details."

    **Follow-up:**

    * "Can you have abstraction without encapsulation? Give an example."
    * "How do Java interfaces and abstract classes serve as abstraction mechanisms differently?"
  </Accordion>

  <Accordion title="What is method overloading vs overriding?">
    * **Overloading:** Same method name, different parameter lists. Resolved at **compile time** (static dispatch).
    * **Overriding:** Subclass provides a specific implementation of a parent method. Resolved at **runtime** (dynamic dispatch).

    **What a strong candidate explains:**

    * **Overloading rules:** Methods must differ in parameter count or types. Return type alone is not sufficient to overload. The compiler selects the most specific matching method. This can cause surprising behavior: `print(null)` with overloads `print(String s)` and `print(Object o)` — the compiler picks `String` because it is more specific. But add `print(Integer i)` and it becomes ambiguous, causing a compile error.
    * **Overriding rules:** The method signature must match exactly. The return type can be **covariant** (a subtype of the parent return type, allowed since Java 5). Access cannot be more restrictive. The `@Override` annotation is not required but is strongly recommended — it catches typos and signature mismatches at compile time. Forgetting `@Override` means you accidentally overload instead of override, and your code silently breaks.
    * **Runtime dispatch:** When you call `animal.speak()` on a `Dog` object referenced as `Animal`, the JVM uses the **virtual method table (vtable)** to find the actual `Dog.speak()` implementation. This lookup has a small overhead, but HotSpot JIT can inline monomorphic call sites (where only one implementation is ever seen) for zero overhead.
    * **The `static` and `private` exception:** Static methods cannot be overridden — they are bound at compile time. If a subclass defines a static method with the same signature, it *hides* (not overrides) the parent method. This is a subtle and commonly tested distinction.

    **What interviewers are really testing:** Whether you understand the compile-time vs runtime resolution mechanism and the edge cases that trip people up.

    **Red flag answer:** "Overloading is same name different params, overriding is same name same params" — correct but shallow, with no mention of resolution timing, vtables, or edge cases.

    **Follow-up:**

    * "What happens if you overload a method where one parameter is `int` and another is `Integer`? How does autoboxing interact with overload resolution?"
    * "Can you override a static method? What happens if you try?"
    * "What is the performance cost of virtual method dispatch, and how does the JIT optimize it?"
  </Accordion>

  <Accordion title="Can you explain 'this' and 'super' keywords?">
    * `this` refers to the current class instance.
    * `super` refers to the parent class members or constructor.

    **What a strong candidate adds:**

    * **`this` uses:** (1) Disambiguate field vs parameter names (`this.name = name` in constructors), (2) Call another constructor in the same class (`this(param)` — must be the first statement), (3) Pass the current instance as an argument (`someMethod(this)`), (4) Return the current instance for fluent APIs (`return this`).
    * **`super` uses:** (1) Call parent constructor (`super()` or `super(args)` — must be first statement in constructor; implicitly called if not specified), (2) Access parent method when overridden (`super.toString()`), (3) Access parent field when shadowed.
    * **Constructor chaining subtlety:** `this()` and `super()` cannot both be in the same constructor because both must be the first statement. If you call `this()` to chain to another constructor, that constructor is responsible for the `super()` call. The compiler ensures every constructor chain eventually calls a `super()`.
    * **Real-world fluent API pattern:** Builder pattern heavily uses `return this`:

    ```java theme={null}
    public Builder withName(String name) {
        this.name = name;
        return this;
    }
    ```

    This enables chaining: `new Builder().withName("foo").withAge(25).build()`.

    **What interviewers are really testing:** Whether you understand constructor chaining mechanics and can articulate the "first statement" constraint.

    **Red flag answer:** Just saying "this is current object, super is parent" without explaining constructor chaining or practical usage patterns.

    **Follow-up:**

    * "Why can't you use `this()` and `super()` in the same constructor?"
    * "What happens if you do not explicitly call `super()` in a subclass constructor?"
  </Accordion>

  <Accordion title="What are constructors in Java?">
    Special methods invoked at object creation time to initialize state. They have the same name as the class and no return type.

    **What a strong candidate explains:**

    * **Types:** Default (no-arg, auto-generated if no constructor defined), parameterized, and copy constructors (manually implemented — Java does not auto-generate these unlike C++).
    * **No-arg constructor gotcha:** If you define *any* constructor, the compiler does **not** generate a default no-arg constructor. This breaks frameworks like Hibernate, JPA, and Jackson that require a no-arg constructor for reflection-based instantiation. This is why you see `@NoArgsConstructor` from Lombok everywhere in Spring Boot projects.
    * **Constructor vs Factory Method:** Effective Java (Item 1) recommends static factory methods over constructors: `Optional.of()`, `List.of()`, `BigInteger.valueOf()`. Advantages: they have names (clearer intent), can return cached instances, can return subtypes, and reduce verbosity with type inference.
    * **Immutability pattern:** For immutable objects, set all fields in the constructor as `final`, provide no setters, and make the class `final`. Java 16+ records (`record Point(int x, int y) {}`) auto-generate this pattern with constructor, `equals()`, `hashCode()`, and `toString()`.
    * **Constructor execution order:** Static blocks run first (once per class load), then instance initializer blocks, then the constructor body. Parent constructors always execute before child constructors. This ordering matters when you have initialization dependencies.

    **What interviewers are really testing:** Whether you understand construction lifecycle, the relationship to frameworks that use reflection, and modern alternatives like records and factory methods.

    **Red flag answer:** "Constructors create objects" with no mention of framework implications, factory methods, or construction order.

    **Follow-up:**

    * "Why does Hibernate require a no-argument constructor, and what happens if you forget it?"
    * "What is the execution order when a subclass with static blocks, instance initializers, and a constructor is instantiated?"
    * "When would you use a static factory method instead of a constructor?"
  </Accordion>
</AccordionGroup>

<hr />

## 3. Java Basics & Control Flow

<AccordionGroup>
  <Accordion title="Difference between == and equals()?">
    `==` compares references (memory addresses) for objects and values for primitives. `.equals()` compares logical content — but only if the class overrides it. The default `Object.equals()` is the same as `==`.

    **What a strong candidate explains:**

    * **The String trap:** `"hello" == "hello"` returns `true` because Java interns string literals (stores them in the **String Pool**). But `new String("hello") == new String("hello")` returns `false` because `new` forces heap allocation, bypassing the pool. This is one of the most common Java interview gotchas.
    * **Contract: `equals()` and `hashCode()` must be consistent.** If two objects are `equals()`, they must have the same `hashCode()`. Violating this breaks `HashMap`, `HashSet`, and any hashing-based collection. Example: you override `equals()` on a `Person` class to compare by name, but forget `hashCode()`. Two `Person("Alice")` objects are "equal" but land in different `HashMap` buckets — so `map.get(new Person("Alice"))` returns `null` even though you just put it in.
    * **`equals()` contract properties:** Reflexive (`x.equals(x)` is true), symmetric (`x.equals(y)` implies `y.equals(x)`), transitive, consistent, and `x.equals(null)` is always false. Implementing this correctly with inheritance is surprisingly hard — which is why Effective Java recommends favoring composition over inheritance for `equals()` correctness.
    * **Modern shortcut:** Java 16+ records auto-generate `equals()` and `hashCode()` based on all fields. Lombok's `@EqualsAndHashCode` does the same. These eliminate an entire class of bugs.

    **What interviewers are really testing:** Whether you know the `equals()`/`hashCode()` contract and can articulate what breaks when it is violated. This separates juniors from mid-level engineers.

    **Red flag answer:** "== checks reference, equals checks value" without mentioning the hashCode contract, String Pool behavior, or what happens when the contract is broken.

    **Follow-up:**

    * "What happens if you override `equals()` but not `hashCode()`? Walk me through the HashMap failure scenario."
    * "Why is implementing `equals()` correctly in a class hierarchy (with inheritance) so difficult?"
    * "How do Java records solve the `equals`/`hashCode` problem?"
  </Accordion>

  <Accordion title="What is a static keyword?">
    `static` denotes class-level members — they belong to the class itself, not to any instance. Used for variables, methods, blocks, nested classes, and imports.

    **What a strong candidate explains:**

    * **Static variables** are shared across all instances. A counter `static int instanceCount` incremented in the constructor tracks how many objects were created. But in multi-threaded environments, a non-synchronized static variable is a race condition waiting to happen. Use `AtomicInteger` or `synchronized` access.
    * **Static methods** cannot access instance members (`this` does not exist). This is why `main()` is static — no object exists yet when the JVM starts. Static methods are good for utility functions (`Math.max()`, `Collections.sort()`), factory methods, and pure functions with no side effects.
    * **Static blocks** execute once when the class is loaded, before any constructor. Used for complex static initialization: loading native libraries (`System.loadLibrary()`), reading configuration, populating lookup tables. Order matters — multiple static blocks execute top to bottom.
    * **Static inner classes** do not hold a reference to the enclosing instance (unlike non-static inner classes). This matters for memory leaks: a non-static inner class (like an anonymous `Handler` in Android) holds an implicit reference to the outer `Activity`, preventing it from being garbage collected. This was one of the most common Android memory leak patterns.
    * **Static imports** (`import static java.lang.Math.PI`) let you use static members without class qualification. Useful for test assertions: `import static org.junit.Assert.*`.

    **What interviewers are really testing:** Whether you understand the class-vs-instance distinction at the JVM level and know the real-world pitfalls (thread safety, memory leaks from inner classes).

    **Red flag answer:** "Static means shared across instances" with no mention of thread safety, static blocks, or the inner class memory leak.

    **Follow-up:**

    * "Why can't you access instance variables from a static method? What would that even mean?"
    * "What is the difference between a static inner class and a non-static inner class in terms of memory?"
    * "How do static blocks interact with class loading? When exactly does a class get loaded?"
  </Accordion>

  <Accordion title="Difference between break and continue?">
    `break` exits the enclosing loop entirely. `continue` skips the remainder of the current iteration and moves to the next one.

    **What a strong candidate adds:**

    * **Labeled break/continue:** Java supports labels for nested loops. `break outerLoop;` exits the outer loop from inside an inner loop — this is the only clean way to break out of nested loops without a flag variable or extracting a method.

    ```java theme={null}
    outerLoop:
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            if (someCondition) break outerLoop;
        }
    }
    ```

    * **`break` in `switch`:** Missing `break` in switch cases causes fall-through — all subsequent cases execute until a break is hit. This is one of the most common Java bugs. Java 14+ switch expressions (`->`) eliminate this problem entirely: `case "A" -> doA();` does not fall through.
    * **Clean code perspective:** Heavy use of `break` and `continue` is often a code smell. In most cases, the logic is clearer when refactored using streams with `filter()`, `findFirst()`, or `takeWhile()`, or by extracting the loop body into a method with an early `return`.

    **What interviewers are really testing:** Awareness of labeled breaks (nested loop control) and the switch fall-through pitfall.

    **Red flag answer:** Basic definition only, no mention of labeled statements or switch fall-through.

    **Follow-up:**

    * "How would you break out of a deeply nested loop? Compare labeled break vs extracting a method."
    * "What changed about `switch` in Java 14+ that makes `break` less of a concern?"
  </Accordion>

  <Accordion title="What is final keyword used for?">
    `final` serves three purposes: makes variables constant (cannot be reassigned), prevents method overriding, and prevents class inheritance.

    **What a strong candidate explains:**

    * **`final` variables:** The reference cannot be reassigned, but the object it points to can still be mutated. `final List<String> list = new ArrayList<>(); list.add("hello");` is perfectly legal. This is a critical distinction — `final` does not mean immutable. For true immutability, you need an unmodifiable collection or a deeply immutable class design.
    * **`final` methods:** Prevent subclasses from overriding. The JVM can also potentially optimize final method calls since it knows no override exists, enabling aggressive inlining. Template Method pattern often uses final methods for the fixed algorithm steps.
    * **`final` classes:** Cannot be extended. `String`, `Integer`, and all wrapper classes are final. This is a security and correctness decision — if you could subclass `String`, you could create a "String" that changes its value after being used as a `HashMap` key, breaking the entire collection.
    * **`final` parameters:** Commonly used in lambda expressions and anonymous classes — Java requires that captured variables be effectively final (their value does not change after initialization). The `final` keyword makes this explicit.
    * **`final` and performance:** Modern JVMs are smart enough to detect effectively final variables without the keyword. The primary benefit of `final` is communicating intent to other developers: "this will not change."

    **What interviewers are really testing:** Whether you understand that `final` on a reference does not mean the object is immutable — this is the most commonly misunderstood aspect.

    **Red flag answer:** "final makes things constant" — this implies immutability, which is incorrect for object references.

    **Follow-up:**

    * "If I have `final Map<String, String> map`, can I add entries to it? How do I make it truly immutable?"
    * "Why is `String` a final class? What would go wrong if you could subclass it?"
    * "What does 'effectively final' mean in the context of lambdas?"
  </Accordion>

  <Accordion title="What are varargs in Java?">
    Variable arguments allow passing zero or more arguments to a method using `...` syntax. Example: `void log(String... messages)`.

    **What a strong candidate explains:**

    * **Under the hood:** Varargs are syntactic sugar for an array. `log("a", "b")` compiles to `log(new String[]{"a", "b"})`. The method receives a regular array internally. This means there is an array allocation on every call — in a tight loop, this creates GC pressure.
    * **Rules:** Only one varargs parameter is allowed per method, and it must be the last parameter. `void process(String name, int... values)` is valid; `void process(int... values, String name)` is not.
    * **Overloading ambiguity:** Varargs can cause confusing overload resolution. If you have `print(int... nums)` and `print(int a, int b)`, calling `print(1, 2)` matches the explicit two-parameter version (more specific). But `print()` matches varargs with zero args. Adding `print(int a)` makes `print(1)` ambiguous in some edge cases.
    * **Heap pollution with generics:** `@SafeVarargs` annotation exists because generic varargs (`T... items`) can cause heap pollution. The compiler warns about this because the varargs array is reifiable (retains runtime type info) but the generic type is erased, creating a type safety hole. This is why `Arrays.asList()` and `List.of()` are annotated with `@SafeVarargs`.

    **What interviewers are really testing:** Whether you know the array allocation under the hood and the generic heap pollution issue — these come up in performance-sensitive and library code.

    **Red flag answer:** Just showing the syntax without mentioning the array allocation, overloading pitfalls, or `@SafeVarargs`.

    **Follow-up:**

    * "What is heap pollution with varargs and generics, and when does `@SafeVarargs` apply?"
    * "What is the performance implication of using varargs in a method called millions of times per second?"
  </Accordion>
</AccordionGroup>

<hr />

## 4. Collections Framework

<AccordionGroup>
  <Accordion title="What is the Java Collections Framework?">
    A unified architecture of interfaces, implementations, and algorithms for storing, retrieving, and manipulating groups of objects. Core interfaces: `Collection` (with subinterfaces `List`, `Set`, `Queue`, `Deque`) and `Map`.

    **What a strong candidate explains:**

    * **The hierarchy matters:** `Map` does not extend `Collection` — this surprises people. It is a separate hierarchy because key-value semantics are fundamentally different from element-in-a-group semantics. But you can get collection views: `map.keySet()`, `map.values()`, `map.entrySet()`.
    * **Interface-based programming:** Always declare variables as the interface type: `List<String> list = new ArrayList<>()`, not `ArrayList<String> list`. This lets you swap implementations without changing client code — fundamental to clean architecture and testability.
    * **Choosing the right implementation:**
      * Need fast random access? `ArrayList` (O(1) get).
      * Need fast insertion/deletion in the middle? `LinkedList` (O(1) with iterator, but O(n) to find position).
      * Need unique elements? `HashSet` (O(1) lookup) or `TreeSet` (O(log n), sorted).
      * Need ordered keys? `TreeMap`. Need insertion order? `LinkedHashMap`. Need thread safety? `ConcurrentHashMap`.
    * **Immutable collections (Java 9+):** `List.of()`, `Set.of()`, `Map.of()` create truly unmodifiable collections. These are not the same as `Collections.unmodifiableList()`, which is just a wrapper — the underlying list can still be mutated through the original reference.
    * **Fail-fast iterators:** Most collections throw `ConcurrentModificationException` if modified during iteration (even in a single thread). This is detected via an internal `modCount`. Concurrent collections like `CopyOnWriteArrayList` use fail-safe iterators that work on a snapshot.

    **What interviewers are really testing:** Whether you can pick the right collection for a given use case and explain why. This is a direct test of data structure knowledge applied to Java's specific implementations.

    **Red flag answer:** "Collections store groups of data" — no mention of the interface hierarchy, choosing implementations, or fail-fast behavior.

    **Follow-up:**

    * "Walk me through how you would choose between ArrayList, LinkedList, HashSet, and TreeSet for a specific use case."
    * "What is the difference between `List.of()` and `Collections.unmodifiableList()`?"
    * "What is a fail-fast iterator and when does `ConcurrentModificationException` happen?"
  </Accordion>

  <Accordion title="Difference between ArrayList and LinkedList?">
    * **ArrayList:** Backed by a dynamic array. O(1) random access, amortized O(1) append, O(n) insertion/deletion in the middle.
    * **LinkedList:** Doubly-linked list. O(n) random access, O(1) insertion/deletion at known positions, implements both `List` and `Deque`.

    **What a strong candidate explains:**

    * **In practice, ArrayList wins almost every time.** Despite LinkedList's theoretical O(1) insertion, you first need O(n) to find the position. ArrayList's contiguous memory layout gives it massive CPU cache advantages — sequential access is 10-100x faster due to cache line prefetching. This is why Java's own documentation and Josh Bloch (who wrote the Collections Framework) say to prefer ArrayList.
    * **ArrayList resizing:** Default initial capacity is 10. When full, it grows by 50% (`newCapacity = oldCapacity + (oldCapacity >> 1)`). This means `Arrays.copyOf()` is called, copying the entire array. If you know the size upfront, `new ArrayList<>(expectedSize)` avoids repeated resizing. For a list that will hold 1 million elements, not pre-sizing means \~20 unnecessary array copies.
    * **LinkedList memory overhead:** Each element is a `Node` object with pointers to `prev`, `next`, and the `item` — about 48 bytes of overhead per element on a 64-bit JVM. An ArrayList element costs \~4-8 bytes (just the reference). For 1 million strings, that is \~48MB of overhead in LinkedList vs \~8MB in ArrayList.
    * **When LinkedList wins:** When used as a `Queue` or `Deque` (add/remove from head and tail are O(1)). But even then, `ArrayDeque` is usually faster because of cache locality. LinkedList's only real advantage is O(1) removal during iteration with `ListIterator`.

    **What interviewers are really testing:** Whether you blindly repeat "LinkedList is better for insertion" or understand that cache locality and real-world performance characteristics make ArrayList the default choice.

    **Red flag answer:** "ArrayList for access, LinkedList for insertion" as a blanket statement. In practice, LinkedList is rarely the right choice.

    **Follow-up:**

    * "Why is ArrayList typically faster than LinkedList even for operations where LinkedList has better Big-O?"
    * "What is `ArrayDeque` and when would you use it instead of `LinkedList`?"
    * "How does ArrayList resize, and what is the performance impact of not pre-sizing?"
  </Accordion>

  <Accordion title="Difference between HashMap and Hashtable?">
    * **HashMap:** Not synchronized, allows one `null` key and multiple `null` values. Part of Java 1.2 Collections Framework.
    * **Hashtable:** Synchronized (thread-safe), does not allow `null` keys or values. Legacy class from Java 1.0.

    **What a strong candidate explains:**

    * **Hashtable is legacy — never use it in new code.** Use `ConcurrentHashMap` for thread safety or `Collections.synchronizedMap()` for a synchronized wrapper. Hashtable synchronizes every operation, which is a performance bottleneck: every `get()` and `put()` acquires a lock, even when there is no contention.
    * **HashMap internals (this is what senior interviews focus on):** HashMap uses an array of "buckets." A key's `hashCode()` is hashed again (bit manipulation to spread hash values), then masked to get a bucket index. Collisions are handled by chaining — a linked list in each bucket. **Since Java 8:** when a bucket has more than 8 entries (and the table has at least 64 buckets), the linked list converts to a **red-black tree**, improving worst-case lookup from O(n) to O(log n). This was added to mitigate hash collision DoS attacks.
    * **Load factor and resizing:** Default initial capacity is 16, load factor is 0.75. When 75% full, the map resizes (doubles capacity) and **rehashes all entries** — this is expensive. For a map that will hold 10,000 entries, initialize with `new HashMap<>(14000)` (account for load factor: 10000 / 0.75 = \~13334).
    * **Key immutability requirement:** If you use a mutable object as a HashMap key and then mutate it, the `hashCode()` changes but the object is still in the old bucket. `get()` will never find it — the entry is effectively lost. This is why `String` is the most common key type (it is immutable and caches its hashCode).

    **What interviewers are really testing:** Whether you understand HashMap's internal structure — bucket array, hash function, collision resolution, the linked-list-to-tree threshold. This is one of the most common senior-level Java questions.

    **Red flag answer:** Just saying "HashMap is not synchronized, Hashtable is" without touching internals. That is a junior-level answer to a question that can go much deeper.

    **Follow-up:**

    * "Walk me through what happens internally when you call `map.put(key, value)` on a HashMap."
    * "Why did Java 8 add treeification to HashMap buckets? What attack does it mitigate?"
    * "What happens if you use a mutable object as a HashMap key and then change it?"
  </Accordion>

  <Accordion title="Explain Set vs List vs Map">
    * **Set:** No duplicates, no guaranteed order (except `TreeSet` and `LinkedHashSet`). Backed by `Map` internally (`HashSet` uses a `HashMap`).
    * **List:** Ordered by index, allows duplicates, supports positional access.
    * **Map:** Key-value pairs, no duplicate keys, each key maps to exactly one value.

    **What a strong candidate explains:**

    * **Set is secretly a Map.** `HashSet` is backed by a `HashMap` where the set elements are keys and all values are a dummy constant `PRESENT` object. `TreeSet` is backed by a `TreeMap`. Understanding this implementation detail explains the O(1) add/contains for HashSet and O(log n) for TreeSet.
    * **Choosing correctly matters at scale:**
      * Need to check "is this element in the collection?" frequently? **HashSet** — O(1) lookup.
      * Need elements sorted? **TreeSet** (red-black tree, O(log n) operations) or **PriorityQueue** (heap, different use case).
      * Need insertion order preserved? **LinkedHashSet** or **LinkedHashMap**.
      * Need positional access (get element at index 5)? **ArrayList**.
      * Need to deduplicate while preserving order? `new LinkedHashSet<>(list)` then back to `new ArrayList<>(set)`.
    * **EnumSet and EnumMap:** If your keys are enum constants, always use these specialized implementations. `EnumSet` uses a bit vector internally — it is orders of magnitude faster than `HashSet<MyEnum>` and uses almost no memory. A set of up to 64 enum values fits in a single `long`.

    **What interviewers are really testing:** Whether you can select the right data structure for a given access pattern and explain the performance characteristics.

    **Red flag answer:** "Set has no duplicates, List has order, Map has key-value" — correct but shows no understanding of implementation trade-offs.

    **Follow-up:**

    * "How is `HashSet` implemented internally? Why does that matter?"
    * "When would you use `EnumSet` or `EnumMap`? What makes them special?"
    * "If you need a collection that is both sorted and allows fast lookup, what do you use?"
  </Accordion>

  <Accordion title="What are Concurrent Collections?">
    Thread-safe collection implementations designed for high-concurrency scenarios. Key ones: `ConcurrentHashMap`, `CopyOnWriteArrayList`, `ConcurrentLinkedQueue`, `BlockingQueue` implementations.

    **What a strong candidate explains:**

    * **`ConcurrentHashMap` (most important to know):** Does not lock the entire map. In Java 7, it used **segment-based locking** (16 segments by default). In Java 8+, it uses **CAS operations (Compare-And-Swap)** and `synchronized` blocks on individual bins (tree or list nodes). This means multiple threads can read and write simultaneously to different bins with no contention. Throughput is dramatically better than `Hashtable` or `Collections.synchronizedMap()` — benchmarks show 5-10x improvement under high concurrency.
    * **`CopyOnWriteArrayList`:** Every write creates a new copy of the underlying array. Reads are lock-free and very fast. Writes are expensive (O(n) copy). Perfect for read-heavy scenarios: configuration lists, listener registries, and observer patterns where writes are rare. Used heavily in Spring's event system.
    * **`BlockingQueue` family:** `ArrayBlockingQueue` (bounded, array-backed), `LinkedBlockingQueue` (optionally bounded, linked-node), `PriorityBlockingQueue` (unbounded, priority-ordered). These are the backbone of producer-consumer patterns. Thread pools internally use `BlockingQueue` for task queuing.
    * **Common mistake:** Wrapping operations in `synchronized` when using `ConcurrentHashMap` defeats the purpose. The check-then-act pattern (`if (!map.containsKey(k)) map.put(k, v)`) is still a race condition — use `putIfAbsent()` or `computeIfAbsent()` instead.

    **What interviewers are really testing:** Whether you understand why `ConcurrentHashMap` is superior to `Hashtable`/`synchronizedMap`, and whether you know the producer-consumer pattern with `BlockingQueue`.

    **Red flag answer:** "ConcurrentHashMap is thread-safe" without explaining how it achieves better performance than locking the whole map.

    **Follow-up:**

    * "How does `ConcurrentHashMap` achieve thread safety without locking the entire map?"
    * "When would you use `CopyOnWriteArrayList` vs `synchronizedList`?"
    * "Implement a simple producer-consumer using `BlockingQueue` — what happens when the queue is full or empty?"
  </Accordion>
</AccordionGroup>

<hr />

## 5. Exception Handling

<AccordionGroup>
  <Accordion title="What is exception handling?">
    A mechanism to handle runtime errors gracefully, maintaining normal application flow using `try`, `catch`, `finally`, `throw`, and `throws`.

    **What a strong candidate explains:**

    * **Exception hierarchy:** `Throwable` is the root. Two subclasses: `Error` (JVM-level problems like `OutOfMemoryError`, `StackOverflowError` — generally not recoverable) and `Exception` (application-level problems). `RuntimeException` extends `Exception` and represents unchecked exceptions.
    * **Try-with-resources (Java 7+):** The modern way to handle resource cleanup. Any class implementing `AutoCloseable` (or `Closeable`) can be declared in the try header:

    ```java theme={null}
    try (var conn = dataSource.getConnection();
         var stmt = conn.prepareStatement(sql)) {
        // use resources
    } // auto-closed in reverse order, even if exception occurs
    ```

    This eliminates 90% of the messy `finally` blocks in pre-Java-7 code and prevents resource leaks. Resources are closed in reverse declaration order.

    * **Exception handling best practices:**
      * Catch specific exceptions, not `Exception` or `Throwable`.
      * Never swallow exceptions silently (`catch (Exception e) {}` is a cardinal sin).
      * Use exception chaining: `throw new BusinessException("Payment failed", cause)` preserves the original stack trace.
      * Do not use exceptions for flow control — `try/catch` is 100-1000x slower than an `if` check.
    * **Multi-catch (Java 7+):** `catch (IOException | SQLException e)` handles multiple exception types in one block, reducing code duplication.

    **What interviewers are really testing:** Whether you write production-quality error handling or just wrap everything in `try/catch(Exception e)`.

    **Red flag answer:** Describing try/catch mechanics without mentioning try-with-resources, exception chaining, or anti-patterns.

    **Follow-up:**

    * "What happens if both the try block and the finally block throw exceptions?"
    * "Why should you never catch `Throwable` in production code?"
    * "How does try-with-resources handle cleanup when multiple resources are opened?"
  </Accordion>

  <Accordion title="Difference between checked and unchecked exceptions?">
    **Checked:** Compiler forces you to handle them (catch or declare with `throws`). Examples: `IOException`, `SQLException`, `ClassNotFoundException`.
    **Unchecked:** Extend `RuntimeException`, not enforced by compiler. Examples: `NullPointerException`, `ArrayIndexOutOfBoundsException`, `IllegalArgumentException`.

    **What a strong candidate explains:**

    * **The philosophical divide:** Checked exceptions represent **recoverable conditions** — the caller can and should take corrective action (retry, use a fallback, notify the user). Unchecked exceptions represent **programming errors** — bugs that should be fixed, not caught (null pointers, array bounds violations).
    * **The controversy:** Checked exceptions are unique to Java — most modern languages (Kotlin, Scala, C#, Python, Go) chose not to include them. The criticism: they create verbose code, leak implementation details into APIs, and force callers to either handle exceptions they cannot meaningfully handle or rethrow them up the stack with ugly `throws` declarations.
    * **Spring's approach:** Spring deliberately wraps checked exceptions in unchecked ones. `SQLException` becomes `DataAccessException` (unchecked). This is a design choice: most callers cannot recover from a database error, so forcing them to catch it adds no value.
    * **Modern best practice:** Use checked exceptions sparingly — only when the caller can genuinely recover. For everything else, unchecked is cleaner. Many senior engineers and framework designers (including Rod Johnson, Spring's creator) advocate this approach.

    **What interviewers are really testing:** Whether you understand the design rationale for checked vs unchecked and can articulate a thoughtful opinion on when to use each.

    **Red flag answer:** "Checked are compile-time, unchecked are runtime" without any opinion on when to use which, or awareness of the industry debate.

    **Follow-up:**

    * "Why did Spring choose to wrap checked exceptions in unchecked ones?"
    * "When would you create a custom checked exception vs an unchecked one?"
    * "How does Kotlin handle the checked exception problem differently from Java?"
  </Accordion>

  <Accordion title="What is finally block used for?">
    Executes regardless of whether an exception was thrown or caught. Used for guaranteed cleanup — closing files, database connections, releasing locks.

    **What a strong candidate explains:**

    * **Execution guarantees:** `finally` runs even if: (1) no exception occurs, (2) an exception is caught, (3) an exception is not caught, (4) there is a `return` statement in try or catch. The only cases where `finally` does not run: `System.exit()`, JVM crash, or infinite loop/deadlock in try/catch.
    * **The return value trap:** If both `try` and `finally` have `return` statements, the `finally` return value wins. This is confusing behavior and a code smell:

    ```java theme={null}
    try { return 1; }
    finally { return 2; } // returns 2, silently discards 1
    ```

    Never return from a finally block. Most static analyzers and IDEs flag this.

    * **Suppressed exceptions:** If `try` throws exception A and `finally` throws exception B, exception A is lost — only B propagates. Try-with-resources fixes this by attaching A as a **suppressed exception** on B, accessible via `getSuppressed()`. This is a real improvement over manual finally blocks.
    * **Modern replacement:** Try-with-resources (`try (resource)`) handles 90%+ of what `finally` was used for, and handles it more correctly. Use `finally` only for non-`AutoCloseable` cleanup or when you need cleanup logic that is not tied to a specific resource.

    **What interviewers are really testing:** Edge cases — return-in-finally, suppressed exceptions, and `System.exit()` behavior. These separate candidates who have debugged weird production issues from those who have only read tutorials.

    **Red flag answer:** "Finally always runs" without mentioning the exceptions to that rule or the return-value trap.

    **Follow-up:**

    * "What happens if both try and finally throw different exceptions? Which one propagates?"
    * "When does finally NOT execute?"
    * "Why is try-with-resources generally preferred over manually writing finally blocks?"
  </Accordion>

  <Accordion title="Explain custom exceptions">
    Create domain-specific exceptions by extending `Exception` (checked) or `RuntimeException` (unchecked) to represent business-level error conditions.

    **What a strong candidate explains:**

    * **When to create one:** When existing exceptions do not convey your domain meaning. `throw new InsufficientFundsException(accountId, amount, balance)` is far more informative than `throw new IllegalStateException("Not enough money")`. It enables callers to catch and handle specific business conditions differently.
    * **Design best practices:**
      * Include relevant context fields (IDs, amounts, timestamps) — not just a message string. This enables structured error handling upstream.
      * Provide constructor overloads: message-only, message+cause, and cause-only for exception chaining.
      * Make custom exceptions `final` if subclassing does not make sense.
      * Consider an exception hierarchy for your domain: `PaymentException` as a base, with `InsufficientFundsException`, `CardDeclinedException`, `FraudDetectedException` as subtypes.
    * **Spring `@ResponseStatus` integration:** Annotate custom exceptions with `@ResponseStatus(HttpStatus.NOT_FOUND)` to automatically map them to HTTP responses. Or use `@ExceptionHandler` and `@ControllerAdvice` for centralized exception-to-response mapping — the production-grade approach.
    * **Anti-patterns to avoid:**
      * Creating exceptions for flow control (`UserNotFoundException` when you should just return `Optional.empty()`).
      * Having a single `ApplicationException` with error codes — this is effectively re-inventing C-style error codes and defeats the purpose of Java's type-based exception hierarchy.
      * Putting business logic in exception constructors.

    **What interviewers are really testing:** API design sense — do you create exceptions that are genuinely useful for callers, or do you create them just because you can?

    **Red flag answer:** "Just extend Exception and add a message" — no mention of context fields, exception hierarchies, or when NOT to create custom exceptions.

    **Follow-up:**

    * "Should `UserNotFoundException` be checked or unchecked? Defend your choice."
    * "How do you map custom exceptions to HTTP responses in a Spring REST API?"
    * "When should you use `Optional` instead of throwing an exception?"
  </Accordion>

  <Accordion title="Difference between throw and throws?">
    * `throw` is used inside a method body to explicitly throw an exception instance: `throw new IllegalArgumentException("invalid")`.
    * `throws` is a method signature declaration that lists exceptions the method may throw: `void read() throws IOException`.

    **What a strong candidate explains:**

    * **`throw` is an action**, `throws` is a contract. `throw` creates and dispatches an exception immediately. `throws` tells the compiler and callers what checked exceptions to expect — it is part of the method's API contract.
    * **Only checked exceptions require `throws`.** You can throw `RuntimeException` subclasses without declaring them. Declaring unchecked exceptions in `throws` is optional but can serve as documentation.
    * **Throws and API design:** Every checked exception in a `throws` clause is a commitment to your callers. Adding one later is a binary-compatible change (existing code still compiles if they do not catch it — wait, no: adding a new checked exception IS a breaking change because callers must now handle it). Removing one is also a breaking change if callers were catching it. This is why checked exceptions in public APIs require careful thought.
    * **Exception translation pattern:** Catch a low-level exception and throw a higher-level one to avoid leaking implementation details. A `UserRepository.findById()` should throw `UserNotFoundException`, not `SQLException` — the caller should not know you are using a SQL database.

    ```java theme={null}
    public User findById(long id) throws UserNotFoundException {
        try {
            return jdbc.query(...);
        } catch (EmptyResultDataAccessException e) {
            throw new UserNotFoundException(id, e); // chained
        }
    }
    ```

    **What interviewers are really testing:** Whether you understand checked exceptions as a contract mechanism and can discuss the API design implications.

    **Red flag answer:** "throw throws the exception, throws declares it" — technically correct but misses the design implications entirely.

    **Follow-up:**

    * "If you add a new checked exception to a public API's `throws` clause, is that a breaking change?"
    * "What is exception translation, and why would you catch one exception only to throw another?"
  </Accordion>
</AccordionGroup>

<hr />

## 6. Multithreading & Concurrency

<AccordionGroup>
  <Accordion title="What is a thread in Java?">
    A thread is a lightweight unit of execution within a process. Multiple threads share the same heap memory but have their own stack. Created by extending `Thread`, implementing `Runnable`, or implementing `Callable`.

    **What a strong candidate explains:**

    * **Three ways to create threads:**
      1. `extends Thread` — tightly couples your task to the thread mechanism. Avoid this: you cannot extend another class.
      2. `implements Runnable` — separates the task from the threading mechanism. Better design, but `run()` cannot return a value or throw checked exceptions.
      3. `implements Callable<V>` — returns a result and can throw checked exceptions. Used with `ExecutorService.submit()`, which returns a `Future<V>`.
    * **Never create raw threads in production.** Use `ExecutorService` (thread pools). Raw threads have no reuse, no bounding, and no backpressure. Creating 10,000 `new Thread()` instances in a burst will likely crash your JVM. Thread pools (`Executors.newFixedThreadPool(n)`, `ThreadPoolExecutor` for fine-tuning) manage thread lifecycle, bound concurrency, and queue excess work.
    * **Java 21 Virtual Threads (Project Loom):** A game-changer. Virtual threads are lightweight (a few KB stack vs \~1MB for platform threads). You can create millions of them. They are scheduled by the JVM, not the OS. This makes the "one-thread-per-request" model viable again for IO-bound services without the complexity of reactive programming. `Thread.ofVirtual().start(() -> ...)` or `Executors.newVirtualThreadPerTaskExecutor()`.
    * **Thread cost:** A platform thread on a 64-bit JVM costs \~1MB of stack memory by default. With 2,000 threads, that is 2GB just in stacks. Plus OS scheduling overhead. This is why reactive frameworks (WebFlux, Vert.x) exist — but virtual threads may make them unnecessary for many use cases.

    **What interviewers are really testing:** Whether you know to use thread pools, understand why raw thread creation is dangerous, and are aware of virtual threads.

    **Red flag answer:** "Create a class that extends Thread and override run()" — this is textbook Java 1.0 and shows no awareness of modern concurrency.

    **Follow-up:**

    * "Why should you never create raw threads in production? What do you use instead?"
    * "What are virtual threads in Java 21, and how do they change the concurrency story?"
    * "Explain the difference between `Runnable` and `Callable`. When would you pick each?"
  </Accordion>

  <Accordion title="Explain the life cycle of a thread">
    **NEW** (created but not started) -> **RUNNABLE** (eligible to run, may or may not be executing) -> **BLOCKED** (waiting for a monitor lock) -> **WAITING** (waiting indefinitely via `wait()`, `join()`, `LockSupport.park()`) -> **TIMED\_WAITING** (waiting with a timeout via `sleep()`, `wait(timeout)`) -> **TERMINATED** (completed execution).

    **What a strong candidate explains:**

    * **RUNNABLE is not RUNNING.** Java does not distinguish between "ready to run" and "actually executing on a CPU core." The OS scheduler decides which runnable threads get CPU time. This is why `Thread.yield()` is just a hint — the scheduler can ignore it. On a single-core machine, only one thread is truly running at any instant.
    * **BLOCKED vs WAITING:** This distinction matters for debugging. BLOCKED means the thread is trying to enter a `synchronized` block/method but another thread holds the lock. WAITING means the thread voluntarily gave up execution (called `wait()`, `join()`, or `park()`). In thread dumps, BLOCKED threads indicate lock contention; WAITING threads are usually expected behavior (threads in a pool waiting for tasks).
    * **How to read a thread dump:** `jstack <pid>` or `kill -3 <pid>` on Linux produces a thread dump showing every thread's state and stack trace. In production incident response, the first thing you do is take 3-5 thread dumps 5 seconds apart and compare them. If the same threads are BLOCKED on the same lock in every dump, you have a lock contention problem. If threads are WAITING on the same object, you may have a deadlock.
    * **State transitions that catch people:**
      * `sleep()` goes to TIMED\_WAITING, not BLOCKED. The thread still holds any locks it acquired.
      * `wait()` goes to WAITING and releases the monitor lock. This is a critical difference from `sleep()`.
      * A thread cannot go from TERMINATED back to RUNNABLE — threads are not restartable.

    **What interviewers are really testing:** Whether you can use thread state knowledge for debugging. The lifecycle diagram is the easy part — applying it to diagnose production concurrency issues is the real skill.

    **Red flag answer:** Listing the states without explaining what causes each transition, or not knowing the difference between BLOCKED and WAITING.

    **Follow-up:**

    * "How do you diagnose a production issue where the application seems frozen? Walk me through your approach."
    * "What is the difference between `sleep()` and `wait()` in terms of lock behavior?"
    * "Can a terminated thread be restarted? What happens if you call `start()` again?"
  </Accordion>

  <Accordion title="Difference between start() and run()?">
    `start()` creates a new OS thread and executes `run()` in that new thread. Calling `run()` directly executes the method in the current thread — no new thread is created.

    **What a strong candidate explains:**

    * **Under the hood:** `start()` calls a native method (`start0()`) that asks the OS to create a new thread, allocate a stack, and begin executing the `run()` method on that new thread. Calling `run()` directly is just a regular method call — no concurrency, no parallelism.
    * **Common bug:** Calling `run()` instead of `start()` is one of the most frequent beginner concurrency bugs. It compiles and runs without error — but everything executes sequentially on the main thread. In testing, this might even produce correct results (no race conditions because there is no concurrency), but performance will be wrong.
    * **Double start:** Calling `start()` on a thread that has already been started throws `IllegalThreadStateException`. A thread object is single-use. If you need to run the same task again, create a new `Thread` instance — or better, submit it to an `ExecutorService` which handles thread reuse.
    * **Debug tip:** If your "concurrent" code is not faster than sequential, check if you are accidentally calling `run()` instead of `start()`.

    **What interviewers are really testing:** Understanding of what actually creates a new thread at the OS level and the subtle bug of calling run() directly.

    **Red flag answer:** "start starts a thread, run runs the code" without explaining the OS-level mechanics or the common mistake.

    **Follow-up:**

    * "What happens if you call `start()` on a thread that has already finished executing?"
    * "In what scenario might calling `run()` directly instead of `start()` actually be useful?"
  </Accordion>

  <Accordion title="What is synchronization?">
    Synchronization ensures that only one thread can access a critical section at a time, preventing race conditions and data corruption on shared mutable state.

    **What a strong candidate explains:**

    * **`synchronized` keyword:** Works on methods (locks on `this` for instance methods, locks on `Class` object for static methods) or blocks (locks on a specified object). Every object in Java has an intrinsic monitor lock (mutex).
    * **The real problem synchronization solves:** Without it, two threads incrementing a shared counter (`count++`) can interleave their read-modify-write operations, losing updates. `count++` is not atomic — it compiles to: load count, increment, store count. Two threads can both read 5, both increment to 6, and store 6 — losing one increment.
    * **Beyond `synchronized` — `java.util.concurrent.locks`:**
      * `ReentrantLock` — same semantics as `synchronized` but with additional features: tryLock (non-blocking attempt), lockInterruptibly (can be interrupted while waiting), and fairness option (FIFO ordering).
      * `ReadWriteLock` — multiple concurrent readers, exclusive writer. Huge throughput improvement for read-heavy workloads (e.g., a cache with 95% reads).
      * `StampedLock` (Java 8) — optimistic read locking for even better read performance.
    * **Atomic classes:** `AtomicInteger`, `AtomicLong`, `AtomicReference` use CAS (Compare-And-Swap) hardware instructions for lock-free thread safety. For a simple counter, `AtomicInteger.incrementAndGet()` is 3-5x faster than `synchronized` under high contention.
    * **Deadlock:** Occurs when two threads each hold a lock the other needs. Classic scenario: Thread A locks resource 1, then tries to lock resource 2. Thread B locks resource 2, then tries to lock resource 1. Both wait forever. Prevention: always acquire locks in a consistent global order.

    **What interviewers are really testing:** Whether you know alternatives to `synchronized` (Lock API, atomics) and can explain deadlock scenarios. This is a must-know for any Java backend role.

    **Red flag answer:** "Use the synchronized keyword" as the complete answer, with no mention of Lock API, atomics, or deadlock.

    **Follow-up:**

    * "What is the advantage of `ReentrantLock` over `synchronized`?"
    * "Explain a deadlock scenario and how you would prevent it."
    * "When would you use `AtomicInteger` instead of `synchronized`?"
  </Accordion>

  <Accordion title="Explain volatile keyword">
    `volatile` guarantees **visibility** — changes to a volatile variable by one thread are immediately visible to all other threads. It prevents the JVM from caching the variable in CPU registers or reordering reads/writes around it.

    **What a strong candidate explains:**

    * **What volatile solves:** Without it, the JVM and CPU may cache a variable's value in a thread-local register. One thread updates the value, but another thread keeps reading the stale cached value. A common symptom: a boolean `running` flag checked in a loop — without `volatile`, the loop may never see the flag change to `false` and run forever.

    ```java theme={null}
    volatile boolean running = true;
    // Thread 1
    while (running) { /* work */ }
    // Thread 2
    running = false; // Thread 1 sees this immediately
    ```

    * **What volatile does NOT solve:** It does not provide atomicity. `volatile int count; count++` is still not thread-safe because `count++` is a compound read-modify-write operation. Two threads can still interleave. For atomic compound operations, use `AtomicInteger` or `synchronized`.
    * **Memory barrier semantics:** A volatile write acts as a "release" fence — all writes before it become visible to other threads. A volatile read acts as an "acquire" fence — all subsequent reads see up-to-date values. This is the foundation of the Java Memory Model's **happens-before** relationship.
    * **Common use cases:** (1) Flags for thread termination (the `running` example above), (2) Double-checked locking for singletons (the `instance` field must be volatile to prevent seeing a partially constructed object), (3) Publishing immutable objects between threads.
    * **Volatile vs synchronized:** Volatile is lighter weight (no lock acquisition) but limited (only visibility, not atomicity). Use volatile for simple flag/state publication; use synchronized or atomics when you need compound operations.

    **What interviewers are really testing:** Whether you understand the Java Memory Model at a basic level — specifically, what "visibility" means and why volatile alone is insufficient for increment operations.

    **Red flag answer:** "Volatile makes variables thread-safe" — this is dangerously wrong. Volatile provides visibility, not atomicity.

    **Follow-up:**

    * "Is `volatile` sufficient to make `count++` thread-safe? Why or why not?"
    * "Explain the Java Memory Model's happens-before relationship in the context of volatile."
    * "Why must the `instance` field in the double-checked locking singleton pattern be volatile?"
  </Accordion>
</AccordionGroup>

<hr />

## 7. Java 8 & Functional Programming

<AccordionGroup>
  <Accordion title="What are lambda expressions?">
    Lambdas provide a concise syntax for anonymous functions: `(parameters) -> expression` or `(parameters) -> { statements; }`. They implement functional interfaces (interfaces with a single abstract method).

    **What a strong candidate explains:**

    * **Not just syntax sugar:** Lambdas are not anonymous inner classes compiled differently. The JVM uses `invokedynamic` (introduced in Java 7 for dynamic languages) to generate lambda implementations at runtime via `LambdaMetafactory`. This means no extra `.class` files, no anonymous class overhead, and the JVM can optimize lambda calls more aggressively (including inlining).
    * **Variable capture:** Lambdas can capture local variables, but only if they are effectively final. This is because the lambda may outlive the stack frame where the variable was declared — the captured value is copied, not referenced. Mutating captured variables would create confusing semantics, so Java forbids it.
    * **Common functional interfaces:**
      * `Predicate<T>` — takes T, returns boolean. Used in `filter()`.
      * `Function<T, R>` — takes T, returns R. Used in `map()`.
      * `Consumer<T>` — takes T, returns void. Used in `forEach()`.
      * `Supplier<T>` — takes nothing, returns T. Used in `orElseGet()`.
      * `BiFunction<T, U, R>` — takes two params, returns one.
    * **Real-world impact:** Lambdas + streams transformed Java code from verbose imperative loops to concise declarative pipelines. A 10-line loop with a temporary list, filter condition, and transformation becomes: `items.stream().filter(i -> i.isActive()).map(Item::getName).collect(toList())`.
    * **Gotcha — exception handling:** Lambdas do not play well with checked exceptions. `Function<String, Integer>` cannot throw `IOException`. You have to either catch inside the lambda (ugly), create custom functional interfaces with `throws`, or use libraries like Vavr that provide checked functional interfaces.

    **What interviewers are really testing:** Whether you understand the implementation mechanism (invokedynamic, not inner classes), variable capture rules, and the standard functional interfaces.

    **Red flag answer:** "Lambdas are short anonymous functions" with no mention of functional interfaces, effectively final, or how they differ from anonymous classes.

    **Follow-up:**

    * "How are lambdas compiled differently from anonymous inner classes?"
    * "Why must captured variables be effectively final?"
    * "How do you handle checked exceptions inside a lambda?"
  </Accordion>

  <Accordion title="What are streams in Java 8?">
    Streams provide a declarative, functional-style API for processing sequences of elements with operations like `filter`, `map`, `reduce`, `collect`, and `flatMap`. They are not data structures — they are pipelines that process data from a source (collection, array, I/O channel).

    **What a strong candidate explains:**

    * **Lazy evaluation:** Intermediate operations (`filter`, `map`, `sorted`) are lazy — they do not execute until a terminal operation (`collect`, `forEach`, `count`, `reduce`) is invoked. This enables **short-circuiting:** `list.stream().filter(x -> x > 10).findFirst()` stops processing as soon as the first match is found, even on a million-element list.
    * **Stream pipeline lifecycle:** Source -> intermediate operations (zero or more) -> terminal operation (exactly one). A stream can only be consumed once — calling a terminal operation "closes" the stream.
    * **Parallel streams:** `list.parallelStream()` or `stream().parallel()` splits work across multiple threads using the common `ForkJoinPool`. **But be careful:** parallel streams have overhead (splitting, thread coordination, merging). They only help for CPU-intensive operations on large datasets. For small collections or IO-bound work, parallel streams are slower. A rule of thumb: at least 10,000 elements and a non-trivial per-element computation.
    * **Collectors:** `Collectors.toList()`, `Collectors.toMap()`, `Collectors.groupingBy()`, `Collectors.partitioningBy()`, `Collectors.joining()`. Custom collectors are possible via `Collector.of()`. `Collectors.groupingBy()` is the stream equivalent of SQL's `GROUP BY` and is incredibly powerful.
    * **Common mistakes:**
      * Modifying the source collection during stream processing (ConcurrentModificationException).
      * Using `forEach` for everything instead of `map` + `collect` — this is "stream abuse" that is worse than a simple for loop.
      * Side effects in intermediate operations (non-deterministic behavior in parallel streams).

    **What interviewers are really testing:** Whether you understand laziness, when parallel streams help vs hurt, and whether you use streams idiomatically (not just as a replacement for for-each loops).

    **Red flag answer:** "Streams let you do map/filter/reduce on collections" without mentioning lazy evaluation, short-circuiting, or parallel stream pitfalls.

    **Follow-up:**

    * "When would you NOT use parallel streams? What determines whether they help?"
    * "What is the difference between `map()` and `flatMap()`? Give a real example."
    * "How does `Collectors.groupingBy()` work? Can you nest collectors?"
  </Accordion>

  <Accordion title="What is Optional class?">
    `Optional<T>` is a container that may or may not hold a non-null value. Introduced in Java 8 to provide a better alternative to returning `null` and to make "absence of value" explicit in the type system.

    **What a strong candidate explains:**

    * **The problem it solves:** `null` is Tony Hoare's "billion dollar mistake." Returning `null` from a method gives callers zero indication that absence is possible. `Optional` makes it explicit: `Optional<User> findById(long id)` clearly communicates that the user might not exist.
    * **Creation:** `Optional.of(value)` (throws NPE if value is null), `Optional.ofNullable(value)` (wraps null safely), `Optional.empty()`.
    * **Usage patterns (good):**

    ```java theme={null}
    // Chaining with map/flatMap
    String city = user.getAddress()
        .flatMap(Address::getCity)
        .map(String::toUpperCase)
        .orElse("UNKNOWN");

    // orElseGet for lazy default (computed only if empty)
    User user = repo.findById(id).orElseGet(() -> createDefault());

    // orElseThrow for mandatory values
    User user = repo.findById(id)
        .orElseThrow(() -> new UserNotFoundException(id));
    ```

    * **Anti-patterns (bad):**
      * Using `Optional` as a method parameter — use overloaded methods or null instead. Optional was designed for return types.
      * Using `Optional` for class fields — it is not `Serializable` and adds overhead. Use nullable fields.
      * Calling `isPresent()` then `get()` — this is just null checking with extra steps. Use `ifPresent()`, `map()`, or `orElse()`.
      * Using `Optional` in collections: `List<Optional<String>>` — just filter out nulls instead.
    * **Performance:** `Optional` creates an object on the heap. In hot paths processing millions of values, the allocation overhead matters. Java may eventually optimize this away with value types (Project Valhalla), but for now, avoid Optional in performance-critical loops.

    **What interviewers are really testing:** Whether you use Optional idiomatically or just as a fancy null wrapper. The anti-patterns reveal your experience level.

    **Red flag answer:** "Optional avoids NullPointerException" followed by `if (opt.isPresent()) opt.get()` — this misses the entire point of the API.

    **Follow-up:**

    * "Why should you not use Optional as a method parameter or a field type?"
    * "What is the difference between `orElse()` and `orElseGet()`? When does it matter?"
    * "How would you refactor a chain of null checks into Optional's functional API?"
  </Accordion>

  <Accordion title="What are method references?">
    A shortcut for lambdas that call an existing method: `ClassName::methodName`. Four types: static (`Math::max`), instance of a particular object (`myObj::toString`), instance of an arbitrary object (`String::toLowerCase`), and constructor (`ArrayList::new`).

    **What a strong candidate explains:**

    * **The four types in detail:**
      1. **Static method reference:** `Integer::parseInt` is equivalent to `s -> Integer.parseInt(s)`.
      2. **Bound instance method:** `System.out::println` is equivalent to `x -> System.out.println(x)`. The instance (`System.out`) is captured.
      3. **Unbound instance method:** `String::length` is equivalent to `s -> s.length()`. The first argument becomes the receiver.
      4. **Constructor reference:** `ArrayList::new` is equivalent to `() -> new ArrayList<>()` or `(capacity) -> new ArrayList<>(capacity)` depending on context.
    * **When to use method references vs lambdas:** Method references are preferred when the lambda simply delegates to an existing method with no additional logic. If you need to transform arguments, add conditions, or call multiple methods, use a lambda.
    * **Gotcha with overloaded methods:** If a method is overloaded, the compiler uses the functional interface's signature to determine which overload to bind. This can sometimes be confusing and may require an explicit lambda for clarity.
    * **Real-world readability:** `names.stream().map(String::toUpperCase).collect(toList())` is more readable than `names.stream().map(s -> s.toUpperCase()).collect(toList())`. But `items.stream().filter(i -> i.getPrice() > 100)` cannot be expressed as a method reference (it has additional logic).

    **What interviewers are really testing:** Whether you know all four types and can reason about when method references improve vs harm readability.

    **Red flag answer:** "It's just `::` instead of arrow" — no awareness of the four forms or when to use lambdas instead.

    **Follow-up:**

    * "What is the difference between a bound and unbound instance method reference?"
    * "Can you use a method reference when the method is overloaded? What happens?"
  </Accordion>

  <Accordion title="Explain functional interfaces">
    An interface with exactly one abstract method (SAM — Single Abstract Method). It can have any number of default or static methods. Annotated with `@FunctionalInterface` (optional but recommended — the compiler enforces the SAM constraint).

    **What a strong candidate explains:**

    * **Core functional interfaces in `java.util.function`:**

    | Interface           | Input | Output  | Use Case            |
    | ------------------- | ----- | ------- | ------------------- |
    | `Predicate<T>`      | T     | boolean | Filtering           |
    | `Function<T,R>`     | T     | R       | Transformation      |
    | `Consumer<T>`       | T     | void    | Side effects        |
    | `Supplier<T>`       | none  | T       | Lazy generation     |
    | `UnaryOperator<T>`  | T     | T       | Same-type transform |
    | `BiFunction<T,U,R>` | T, U  | R       | Two-input transform |

    * **`@FunctionalInterface` is a compile-time guard:** Like `@Override`, it is not required for functionality, but it prevents accidental addition of a second abstract method. Without it, adding `void anotherMethod()` silently breaks all lambda call sites — with it, the compiler catches the error immediately.
    * **Composition via default methods:** `Predicate` has `and()`, `or()`, `negate()`. `Function` has `compose()` and `andThen()`. This enables building complex behaviors from simple building blocks:

    ```java theme={null}
    Predicate<String> isLong = s -> s.length() > 10;
    Predicate<String> startsWithA = s -> s.startsWith("A");
    Predicate<String> combined = isLong.and(startsWithA);
    ```

    * **Existing functional interfaces you already know:** `Runnable` (zero args, void), `Callable<V>` (zero args, returns V), `Comparator<T>` (two args, returns int). These predate Java 8 but are now usable as lambda targets.
    * **Primitive specializations:** `IntPredicate`, `LongFunction`, `DoubleSupplier` avoid autoboxing overhead. In performance-critical code, prefer `IntStream` with `IntPredicate` over `Stream<Integer>` with `Predicate<Integer>`.

    **What interviewers are really testing:** Whether you can name the core functional interfaces, know their use in the stream API, and understand composition.

    **Red flag answer:** "An interface with one method that you can use with lambdas" — too vague, no mention of specific interfaces or composition.

    **Follow-up:**

    * "Why do primitive specializations like `IntPredicate` exist alongside `Predicate<Integer>`?"
    * "How would you compose multiple predicates together? Show the API."
    * "What happens if you accidentally add a second abstract method to a functional interface that is used in 50 places?"
  </Accordion>
</AccordionGroup>

<hr />

## 8. Memory Management & Garbage Collection

<AccordionGroup>
  <Accordion title="How does garbage collection work?">
    The JVM automatically reclaims memory by identifying and removing objects that are no longer reachable from any GC root (stack variables, static fields, JNI references). You can suggest GC with `System.gc()`, but the JVM is free to ignore it.

    **What a strong candidate explains:**

    * **Generational hypothesis:** Most objects die young. The JVM exploits this by dividing the heap into generations:
      * **Young Generation (Eden + Survivor S0/S1):** New objects are allocated in Eden. When Eden fills, a **Minor GC** runs, copying surviving objects to a Survivor space. Objects that survive multiple Minor GCs (default threshold: 15 cycles) are promoted to Old Generation.
      * **Old Generation (Tenured):** Long-lived objects. Collected during **Major GC** (or Full GC), which is much more expensive — can pause the application for seconds.
    * **GC algorithms (know at least 3):**
      * **G1 (Garbage First):** Default since Java 9. Region-based, targets a configurable pause time (`-XX:MaxGCPauseMillis=200`). Good general-purpose collector.
      * **ZGC:** Ultra-low-latency (\<1ms pauses) even with multi-terabyte heaps. Uses colored pointers and load barriers. Production-ready since Java 15.
      * **Shenandoah:** Similar goals to ZGC, concurrent compaction. Available in OpenJDK.
      * **Parallel GC:** Throughput-optimized, longer pauses. Good for batch processing.
      * **Serial GC:** Single-threaded, for small heaps/containers.
    * **GC tuning in practice:** At a high-throughput service processing 50K requests/sec, GC pauses directly impact p99 latency. Steps: (1) Enable GC logging (`-Xlog:gc*`), (2) Analyze with GCViewer or GCEasy, (3) Identify if pauses are Minor or Full GC, (4) Tune heap size and generation ratios, (5) Consider switching collector (G1 to ZGC for latency-sensitive services).
    * **Never call `System.gc()` in production.** It triggers a Full GC that can pause the application for seconds. Some frameworks (like DirectByteBuffer cleanup) use it internally, but application code should not.

    **What interviewers are really testing:** Whether you understand generational collection, can name specific collectors and their trade-offs, and have any experience with GC tuning.

    **Red flag answer:** "Java has a garbage collector that frees memory automatically" — no mention of generations, collectors, or tuning.

    **Follow-up:**

    * "What is the difference between Minor GC and Full GC? Which one should you worry about?"
    * "When would you choose ZGC over G1? What are the trade-offs?"
    * "Walk me through how you would investigate a Java service with p99 latency spikes caused by GC."
  </Accordion>

  <Accordion title="What are strong, weak, and soft references?">
    Reference types that control how aggressively the GC can collect objects:

    * **Strong:** Normal references (`Object o = new Object()`). GC will not collect as long as a strong reference exists.
    * **Soft:** Collected only when the JVM is running low on memory. Created via `SoftReference<T>`.
    * **Weak:** Collected at the next GC cycle if only weakly reachable. Created via `WeakReference<T>`.
    * **Phantom:** Cannot access the referent. Used for cleanup actions before finalization. Created via `PhantomReference<T>` with a `ReferenceQueue`.

    **What a strong candidate explains:**

    * **Soft references for caches:** `SoftReference` is ideal for memory-sensitive caches. The object stays in memory as long as there is plenty of heap space, but is eligible for collection under memory pressure. Example: an image cache that holds images in memory when possible but lets the GC reclaim them before throwing `OutOfMemoryError`. Guava's `CacheBuilder.softValues()` uses this.
    * **Weak references for metadata:** `WeakHashMap` uses weak keys — entries are automatically removed when the key is no longer strongly referenced elsewhere. Common use case: associating metadata with objects without preventing their GC. `ThreadLocal` cleanup also relies on weak references.
    * **Phantom references for finalization replacement:** Phantom references are enqueued in a `ReferenceQueue` after the referent is finalized. You can use a daemon thread to poll the queue and perform cleanup. This is the modern replacement for the deprecated `finalize()` method. Java 9+ `Cleaner` API wraps this pattern.
    * **Real-world production bug:** A common memory leak pattern: using a `Map<Key, Value>` where the Keys should be garbage collected but the Map prevents it. Switching to `WeakHashMap` or using `Caffeine` cache with weak keys fixes the leak.

    **What interviewers are really testing:** Whether you know practical use cases for each reference type, especially caching with soft references and metadata association with weak references.

    **Red flag answer:** Listing the types without explaining when you would use each one in practice.

    **Follow-up:**

    * "How would you implement a memory-sensitive cache using soft references?"
    * "What is `WeakHashMap` and when would you use it?"
    * "What replaced `finalize()` in modern Java, and how does it use phantom references?"
  </Accordion>

  <Accordion title="What are memory leaks in Java?">
    Memory leaks in Java occur when objects are no longer needed by the application but are still referenced, preventing the garbage collector from reclaiming them. The heap grows until `OutOfMemoryError`.

    **What a strong candidate explains:**

    * **Common leak patterns:**
      1. **Static collections:** `static List<Object> cache = new ArrayList<>();` that grows forever. Static fields live as long as the ClassLoader (effectively forever in most apps).
      2. **Unclosed resources:** Database connections, file handles, HTTP clients not closed. Connection pools exhaust, file descriptor limits hit.
      3. **Listener/callback registration without deregistration:** Registering an event listener and never removing it. The listener holds a reference to its enclosing object. Common in GUI applications and Spring event handling.
      4. **Inner class references:** Non-static inner classes hold an implicit reference to the enclosing instance. If the inner class instance outlives the outer, the outer cannot be collected.
      5. **ThreadLocal variables:** Not calling `remove()` in thread pools. The thread lives forever (pool), the ThreadLocal value lives forever, and everything it references lives forever. This caused massive memory leaks at several companies running web apps on Tomcat.
      6. **String.intern() abuse:** Interning user-provided strings (like session IDs) fills the String Pool permanently.
    * **Detection tools:**
      * **Heap dumps:** `jmap -dump:format=b,file=heap.hprof <pid>` or `-XX:+HeapDumpOnOutOfMemoryError` (automatic dump on OOM).
      * **Analysis:** Eclipse MAT (Memory Analyzer Tool), VisualVM, YourKit. Look at the "dominator tree" to find which objects retain the most memory.
      * **Monitoring:** Track heap usage over time with JMX, Micrometer, or Prometheus. A steadily increasing heap between Full GCs (the "sawtooth" pattern with rising baseline) indicates a leak.
    * **A war story:** ThreadLocal memory leak in a Spring Boot app running on embedded Tomcat. Each request set a `ThreadLocal<UserContext>` but never called `remove()`. With a thread pool of 200 threads, this leaked 200 `UserContext` objects (each holding a database session). Over days, the heap grew by hundreds of MB. Fix: `try/finally` with `threadLocal.remove()`, or a servlet filter that clears ThreadLocals after each request.

    **What interviewers are really testing:** Whether you can enumerate specific leak patterns AND describe how to detect them with real tools. Production experience with heap dump analysis is a strong signal.

    **Red flag answer:** "Memory leaks happen when objects are not freed" — no specific patterns, no tools, no production experience.

    **Follow-up:**

    * "How do you take and analyze a heap dump? Walk me through the process."
    * "What is the ThreadLocal memory leak pattern, and how do you prevent it?"
    * "How can you tell from monitoring data that you have a memory leak vs just a too-small heap?"
  </Accordion>

  <Accordion title="How to prevent OutOfMemoryError?">
    Prevention involves a combination of proper coding practices, JVM configuration, and monitoring.

    **What a strong candidate explains:**

    * **Types of OOM (they are not all the same):**
      * `Java heap space` — the heap is full. Most common. Increase `-Xmx` or fix the leak.
      * `GC overhead limit exceeded` — GC running constantly but freeing very little memory. Usually means the heap is nearly full with live objects.
      * `Metaspace` — too many classes loaded (common with dynamic proxies, heavy reflection, or classloader leaks in app servers). Increase `-XX:MaxMetaspaceSize`.
      * `unable to create native thread` — OS thread limit hit. Reduce thread count or increase `ulimit -u`.
      * `Direct buffer memory` — too many `ByteBuffer.allocateDirect()` calls. Increase `-XX:MaxDirectMemorySize`.
    * **Prevention strategies:**
      1. **Size your heap correctly:** Use load testing to determine steady-state memory usage. Set `-Xmx` to 2-3x steady state. Never set it to the maximum available RAM — leave room for the OS, metaspace, thread stacks, and direct buffers.
      2. **Use streaming for large data:** Process files and query results with streams/iterators instead of loading everything into memory. A 2GB CSV should be read line by line, not loaded into `List<String>`.
      3. **Object pooling and caching with eviction:** Use bounded caches (Caffeine, Guava) with size limits and TTL. Never use an unbounded `HashMap` as a cache.
      4. **Close resources:** Try-with-resources for everything. Connection pool limits prevent connection object accumulation.
      5. **Monitor in production:** `-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof` automatically captures a heap dump when OOM occurs. Set up alerting on heap usage trends.
    * **JVM flags every Java developer should know:**
      * `-Xms` / `-Xmx`: Initial and max heap size.
      * `-XX:+UseG1GC` or `-XX:+UseZGC`: GC algorithm selection.
      * `-Xlog:gc*`: GC logging.
      * `-XX:+HeapDumpOnOutOfMemoryError`: Auto heap dump.
      * `-XX:MaxMetaspaceSize`: Metaspace cap.

    **What interviewers are really testing:** Whether you know that OOM has different types with different causes and solutions, and whether you have a monitoring/diagnosis strategy.

    **Red flag answer:** "Increase heap size" as the only answer, with no mention of OOM types, monitoring, or coding practices.

    **Follow-up:**

    * "What is the difference between `Java heap space` and `GC overhead limit exceeded` OOM errors?"
    * "How do you size the JVM heap for a containerized application?"
    * "What JVM flags do you always set in production and why?"
  </Accordion>

  <Accordion title="Explain finalize() method">
    `finalize()` was a method called by the GC before collecting an object, intended for cleanup. It is **deprecated since Java 9 and removed for removal in Java 18**. Never use it.

    **What a strong candidate explains:**

    * **Why finalize() is terrible:**
      1. **Non-deterministic:** You have no idea when (or if) `finalize()` will be called. The GC runs on its own schedule. Objects with finalizers require at least two GC cycles to be collected — they are put on a finalization queue, finalized, then collected in the next cycle.
      2. **Performance penalty:** Objects with finalizers cannot be allocated in thread-local allocation buffers (TLABs) on some JVMs. They slow down GC by 50-100x for those objects.
      3. **Resurrection risk:** Inside `finalize()`, you can store `this` in a static field, "resurrecting" the object. This creates bizarre bugs and confuses the GC.
      4. **No ordering guarantees:** If object A references object B and both have finalizers, there is no guarantee which finalizer runs first. A's finalizer might access B after B has already been finalized.
      5. **Exceptions are swallowed:** If `finalize()` throws an exception, it is silently ignored. No logging, no notification.
    * **Modern alternatives:**
      * **`try-with-resources` + `AutoCloseable`:** The primary mechanism for deterministic cleanup. Close resources when you are done, not when the GC gets around to it.
      * **`Cleaner` (Java 9+):** Registers cleaning actions that run when an object becomes phantom-reachable. Used by JDK internals (DirectByteBuffer cleanup). Better than finalize() but still non-deterministic — use as a safety net, not primary cleanup.
    * **If you see `finalize()` in a codebase,** it is almost certainly a bug or legacy code that needs refactoring.

    **What interviewers are really testing:** Whether you know why `finalize()` is harmful and what the modern alternatives are. Advocating for `finalize()` is a red flag.

    **Red flag answer:** "finalize() cleans up before GC" without mentioning that it is deprecated, slow, and dangerous.

    **Follow-up:**

    * "What is the `Cleaner` API introduced in Java 9, and how does it improve on `finalize()`?"
    * "Why does an object with a finalizer take at least two GC cycles to be collected?"
    * "What is the modern best practice for ensuring a resource (like a native handle) gets cleaned up?"
  </Accordion>
</AccordionGroup>

<hr />

## 9. Spring & Framework Concepts

<AccordionGroup>
  <Accordion title="What is Spring Framework?">
    Spring is a comprehensive application framework for Java that provides dependency injection (Inversion of Control), aspect-oriented programming, transaction management, and integration with databases, messaging, and web technologies.

    **What a strong candidate explains:**

    * **Core philosophy:** Spring inverts control — instead of your code creating dependencies, the framework creates and wires them. This makes code testable (inject mocks), loosely coupled (depend on interfaces), and configurable (swap implementations without code changes).
    * **Key modules:**
      * **Spring Core/IoC:** The DI container. The foundation everything else builds on.
      * **Spring MVC:** Web framework for building REST APIs and server-rendered pages.
      * **Spring Data:** Abstracts database access (JPA, MongoDB, Redis, Elasticsearch) with repository interfaces.
      * **Spring Security:** Authentication and authorization.
      * **Spring AOP:** Cross-cutting concerns (logging, transactions, security) via proxies.
      * **Spring Cloud:** Distributed systems patterns (service discovery, config server, circuit breakers).
    * **Why companies use Spring:** It is the dominant Java backend framework because of its ecosystem maturity, extensive documentation, massive community, and opinionated-but-flexible design. Companies like Netflix (until they migrated parts to Go/Node), Amazon (internal services), and most Fortune 500 Java shops use Spring.
    * **Spring vs Spring Boot:** Spring is the framework. Spring Boot is the opinionated auto-configuration layer on top. Spring Boot eliminated the "XML hell" of early Spring with sensible defaults, embedded servers, and starter dependencies.

    **What interviewers are really testing:** Whether you understand the IoC principle as a design philosophy, not just as "annotations that make things work."

    **Red flag answer:** "Spring is a framework for building web apps" — too vague, no mention of IoC or how Spring's DI container actually provides value.

    **Follow-up:**

    * "Explain Inversion of Control in Spring without using the word 'annotation' — what is the underlying concept?"
    * "How does Spring create and manage bean instances? Walk me through the lifecycle."
    * "What problem does Spring solve that you could not solve with plain Java?"
  </Accordion>

  <Accordion title="Explain dependency injection (DI)">
    DI is a design pattern where an object receives its dependencies from an external source (the container) rather than creating them internally. Spring implements DI via constructor injection, setter injection, and field injection.

    **What a strong candidate explains:**

    * **Three injection types (and which to prefer):**
      1. **Constructor injection (recommended):** Dependencies are provided via the constructor. Fields can be `final`, ensuring immutability. All dependencies are required — the object cannot be created in an invalid state. Since Spring 4.3, `@Autowired` is optional on single-constructor beans.
      2. **Setter injection:** Dependencies set via setter methods. Use for optional dependencies. Allows reconfiguration after construction (rarely needed).
      3. **Field injection (`@Autowired` on fields):** Convenient but problematic — cannot make fields `final`, hides dependencies (not visible in constructor), makes unit testing harder (need reflection or Spring test context to inject).
    * **Why constructor injection wins:** It makes dependencies explicit (you see them in the constructor signature), enforces immutability (`final` fields), and enables easy testing (just call the constructor with mocks — no Spring context needed).

    ```java theme={null}
    @Service
    public class OrderService {
        private final PaymentGateway paymentGateway;
        private final OrderRepository orderRepository;

        // @Autowired is optional with single constructor
        public OrderService(PaymentGateway paymentGateway,
                            OrderRepository orderRepository) {
            this.paymentGateway = paymentGateway;
            this.orderRepository = orderRepository;
        }
    }
    ```

    * **Circular dependency problem:** If Bean A depends on Bean B and Bean B depends on Bean A, Spring cannot construct either. Spring 5 throws an error at startup for constructor injection circular dependencies (earlier versions silently used field injection workarounds). The fix: redesign — circular dependencies almost always indicate a design flaw. Extract the shared logic into a third bean.
    * **`@Qualifier` and `@Primary`:** When multiple beans implement the same interface, use `@Qualifier("beanName")` to select the specific one, or `@Primary` to designate a default.

    **What interviewers are really testing:** Whether you prefer constructor injection and can explain why. Field injection preference is a yellow flag for code quality awareness.

    **Red flag answer:** "`@Autowired` on a field" as the primary approach, with no awareness of constructor injection or why it is preferred.

    **Follow-up:**

    * "Why is constructor injection preferred over field injection?"
    * "How do you handle circular dependencies in Spring? Is there a design-level fix?"
    * "How would you inject different implementations of the same interface depending on the profile or environment?"
  </Accordion>

  <Accordion title="What is the difference between BeanFactory and ApplicationContext?">
    * **BeanFactory:** The basic IoC container. Provides bean creation and wiring. Lazy initialization by default.
    * **ApplicationContext:** Extends BeanFactory with enterprise features. Eager initialization by default.

    **What a strong candidate explains:**

    * **ApplicationContext adds:**
      * **Event publishing:** `ApplicationEventPublisher` for the observer pattern. Publish custom domain events (`OrderCreatedEvent`) and have any number of listeners react without coupling.
      * **Internationalization (i18n):** `MessageSource` for localized messages.
      * **Environment abstraction:** Access to properties, profiles (`@Profile("prod")`), and property sources.
      * **AOP integration:** Automatic proxy creation for `@Transactional`, `@Cacheable`, `@Async`.
      * **Bean lifecycle hooks:** `@PostConstruct`, `@PreDestroy`, `BeanPostProcessor`, `BeanFactoryPostProcessor`.
    * **Lazy vs eager initialization:** BeanFactory creates beans on first request (lazy). ApplicationContext creates all singleton beans at startup (eager). This means ApplicationContext fails fast — misconfiguration errors surface at startup, not at runtime. This is a production advantage: better to fail in deployment than at 3 AM under load.
    * **In practice, you always use ApplicationContext.** Specifically, `AnnotationConfigApplicationContext` for standalone apps or `SpringApplication.run()` in Spring Boot (which creates a `WebApplicationContext`). Direct BeanFactory use is essentially never needed in modern Spring.
    * **Bean scopes:** `singleton` (default — one instance per container), `prototype` (new instance per request), `request` (one per HTTP request), `session` (one per HTTP session). Understanding scopes is critical: injecting a prototype-scoped bean into a singleton gives you the same prototype instance forever (must use `ObjectProvider<T>` or method injection for correct behavior).

    **What interviewers are really testing:** Whether you understand bean lifecycle, scopes, and the practical implications — especially the singleton-with-prototype scope pitfall.

    **Red flag answer:** "ApplicationContext has more features than BeanFactory" without naming specific features or understanding the eager vs lazy distinction.

    **Follow-up:**

    * "What happens if you inject a prototype-scoped bean into a singleton? How do you fix it?"
    * "How do bean lifecycle callbacks (`@PostConstruct`, `@PreDestroy`) work? When do they execute?"
    * "What is a `BeanPostProcessor` and when would you write a custom one?"
  </Accordion>

  <Accordion title="Explain Spring Boot">
    Spring Boot is an opinionated extension of Spring that simplifies configuration with auto-configuration, embedded servers (Tomcat, Jetty, Undertow), starter dependencies, and production-ready features (Actuator, metrics).

    **What a strong candidate explains:**

    * **Auto-configuration magic:** Spring Boot examines your classpath and automatically configures beans. If `spring-boot-starter-data-jpa` is on the classpath and you have a `DataSource` configured, Spring Boot auto-creates `EntityManagerFactory`, `TransactionManager`, and repository beans. The key: `@EnableAutoConfiguration` (included in `@SpringBootApplication`) triggers `META-INF/spring.factories` scanning (or `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` in 3.0+).
    * **Starter dependencies:** Curated dependency bundles. `spring-boot-starter-web` pulls in Spring MVC, Jackson, Tomcat, validation. `spring-boot-starter-test` pulls in JUnit 5, Mockito, AssertJ, Spring Test. This eliminates dependency version conflicts — the Boot BOM (Bill of Materials) ensures compatible versions.
    * **Externalized configuration:** Application properties (`application.yml` or `.properties`) with profiles (`application-prod.yml`), environment variables, command-line arguments. Configuration precedence order matters: command-line args > env vars > `application-{profile}.yml` > `application.yml`. Twelve-Factor App compliance out of the box.
    * **Spring Boot Actuator:** Production monitoring endpoints: `/actuator/health` (for load balancers), `/actuator/metrics` (Micrometer metrics), `/actuator/info`, `/actuator/env`. Integrates with Prometheus, Datadog, New Relic. In production, you expose `/health` publicly and secure everything else.
    * **Spring Boot 3.0 changes:** Requires Java 17+, migrated from Java EE (`javax.*`) to Jakarta EE (`jakarta.*`), native compilation support via GraalVM (AOT compilation for sub-second startup — important for serverless/FaaS).

    **What interviewers are really testing:** Whether you understand how auto-configuration works under the hood, not just that it "magically works."

    **Red flag answer:** "Spring Boot makes Spring easier" without explaining auto-configuration, starters, or Actuator.

    **Follow-up:**

    * "How does Spring Boot auto-configuration work internally? What triggers it?"
    * "How do you override or exclude a specific auto-configuration?"
    * "What is Spring Native / GraalVM native image, and when would you use it?"
  </Accordion>

  <Accordion title="What are common Spring annotations?">
    `@Component`, `@Service`, `@Repository`, `@Controller`, `@RestController`, `@Autowired`, `@Configuration`, `@Bean`, `@Value`, `@Transactional`, `@Profile`.

    **What a strong candidate explains (not just lists):**

    * **Stereotype annotations (component scanning):**
      * `@Component` — generic Spring-managed bean.
      * `@Service` — business logic layer. No additional behavior over `@Component`, but signals intent.
      * `@Repository` — data access layer. Adds automatic exception translation: vendor-specific exceptions (like Hibernate's `ConstraintViolationException`) are wrapped into Spring's `DataAccessException` hierarchy.
      * `@Controller` — MVC controller, returns views. `@RestController` = `@Controller` + `@ResponseBody` (returns JSON/XML directly).
    * **Configuration annotations:**
      * `@Configuration` — declares a bean definition source. Methods annotated with `@Bean` produce Spring-managed instances. `@Configuration` classes are CGLIB-proxied so that `@Bean` methods calling other `@Bean` methods return the same singleton (not a new instance).
      * `@Bean` — method-level bean declaration, used when you cannot annotate the source class (third-party libraries).
    * **Behavior annotations:**
      * `@Transactional` — wraps the method in a database transaction via AOP proxy. **Gotcha:** `@Transactional` does not work on `private` methods (proxy cannot intercept) or on self-invocation (calling `this.method()` bypasses the proxy). This causes silent transaction bugs that are hard to debug.
      * `@Async` — executes the method in a separate thread. Same proxy limitations as `@Transactional`.
      * `@Cacheable` — caches the return value. `@CacheEvict` clears the cache.
    * **The proxy trap (critical to understand):** Spring creates a proxy around your bean to implement `@Transactional`, `@Async`, `@Cacheable`. When you call a method on `this` (self-invocation), you bypass the proxy, and the annotation has no effect. This is the #1 Spring annotation gotcha in production.

    **What interviewers are really testing:** Whether you know the proxy-based implementation and its limitations, especially the self-invocation trap with `@Transactional`.

    **Red flag answer:** Listing annotations without explaining `@Transactional` proxy limitations or the difference between `@Repository` and `@Component`.

    **Follow-up:**

    * "Why does `@Transactional` not work when you call it from within the same class? How do you fix it?"
    * "What is the difference between `@Component` and `@Bean`? When would you use each?"
    * "How does `@Repository` exception translation work?"
  </Accordion>
</AccordionGroup>

<hr />

## 10. Java Interview Best Practices

<AccordionGroup>
  <Accordion title="Common performance optimization tips">
    **What a strong candidate discusses (with specifics, not generic advice):**

    * **Data structure selection matters most.** Switching from `LinkedList` to `ArrayList` for random access, from `HashMap` to `EnumMap` for enum keys, or from `TreeMap` to `HashMap` when sorting is not needed can yield 2-10x improvement with zero algorithmic changes.
    * **String concatenation in loops:** Never use `+` in a loop — it creates a new `String` object each iteration. Use `StringBuilder` for explicit appending. Java's compiler optimizes single-statement concatenation (`"a" + b + "c"`) but not loop concatenation.
    * **Stream vs loop performance:** Streams add overhead (object creation for lambdas, stream pipeline setup). For simple operations on small collections (\<1000 elements), a for-loop is faster. For large collections or parallelizable operations, streams can be faster. Benchmark, do not assume.
    * **Connection pooling:** Never create database connections per request. Use HikariCP (Spring Boot default) with appropriate pool sizing. A formula from HikariCP's wiki: `connections = ((core_count * 2) + effective_spindle_count)`. For an 8-core server with SSDs, start with \~20 connections.
    * **Avoid premature optimization.** Profile first with async-profiler, JFR (Java Flight Recorder), or YourKit. Identify actual hotspots. The 80/20 rule applies: 80% of time is spent in 20% of code. Optimizing the wrong code wastes engineering time.
    * **JVM flags:** `-XX:+UseG1GC` (or `-XX:+UseZGC` for low latency), `-Xms` and `-Xmx` set to the same value (avoids resize pauses), `-XX:+AlwaysPreTouch` (page-in memory at startup, avoids latency spikes later).

    **What interviewers are really testing:** Whether you optimize based on measurement or gut feeling, and whether you know specific techniques beyond "use caching."

    **Red flag answer:** "Cache everything and use async" — vague and potentially harmful without context.

    **Follow-up:**

    * "How would you profile a Java application that is slower than expected?"
    * "What is the HikariCP connection pool sizing formula, and why does it work?"
  </Accordion>

  <Accordion title="Common mistakes to avoid">
    **What a strong candidate highlights (with war stories):**

    * **Forgetting `equals()` and `hashCode()`:** Put an object in a `HashSet`, mutate a field used in `equals()`, and the set now contains a "ghost" entry you can never find or remove. Tools: Lombok `@EqualsAndHashCode`, IDE generation, or Java records.
    * **Neglecting resource closure:** Pre-Java-7 code littered with `finally` blocks that themselves threw exceptions. Modern fix: always use try-with-resources. Spotbugs and SonarQube flag unclosed resources automatically.
    * **Ignoring concurrency:** Using `HashMap` in a multithreaded context causes infinite loops (the internal linked list can form a cycle during concurrent resize — this was a real production incident at many companies running Java 7). Fix: `ConcurrentHashMap`.
    * **Catching `Exception` broadly:** `catch (Exception e) { log.error("error", e); }` masks bugs. Catch specific exceptions and handle them appropriately. Let unexpected exceptions propagate.
    * **Mutable objects as Map keys:** See the `equals()`/`hashCode()` point above. Always use immutable keys (String, Integer, records, value objects).
    * **Blocking in reactive pipelines:** If you use WebFlux or any reactive framework, calling `Thread.sleep()`, synchronous JDBC, or `synchronized` blocks destroys throughput by blocking the event loop thread. Use reactive drivers (R2DBC) and non-blocking APIs.
    * **Not configuring thread pool sizes:** Default `ForkJoinPool.commonPool()` has `availableProcessors() - 1` threads. If your parallel streams share this pool with other framework code and block on IO, the entire pool stalls.

    **What interviewers are really testing:** Whether you have been bitten by these in production and learned from it.

    **Red flag answer:** Generic list of "don't forget null checks" — no specific Java pitfalls.

    **Follow-up:**

    * "Have you encountered a HashMap infinite loop bug? Explain how it happens at the implementation level."
    * "How do you prevent mutable-key bugs in a large codebase?"
  </Accordion>

  <Accordion title="How to prepare for coding interviews?">
    **What a strong candidate recommends (structured approach):**

    * **Data structures & algorithms in Java:** Focus on arrays, strings, HashMaps, trees, graphs, and dynamic programming. Practice on LeetCode (aim for 150-200 medium problems). Know Java's built-in data structures cold: `PriorityQueue` for heaps, `Deque` for stacks/queues, `TreeMap` for ordered maps with `floorKey()`/`ceilingKey()`.
    * **Java-specific interview patterns:**
      * Know the Collections API deeply: `Collections.sort()` with `Comparator`, `Map.merge()`, `Map.computeIfAbsent()`, `List.subList()`.
      * Master Streams for data processing questions: `groupingBy`, `toMap`, `flatMap`, `reduce`.
      * String manipulation: `StringBuilder`, `String.chars()`, regex with `Pattern` and `Matcher`.
    * **System design with Java specifics:** Discuss thread pools, connection pools, caching (Caffeine, Redis), message queues (Kafka), load balancing, and database sharding. Show you know how to implement these in Spring Boot.
    * **Build a portfolio project:** A Spring Boot REST API with JPA, proper exception handling, validation, testing (JUnit 5 + Mockito), Docker, and CI/CD. This demonstrates production readiness.
    * **Mock interviews:** Practice explaining your thought process out loud. Interviewers evaluate communication as much as correctness. Use the "clarify, approach, code, test" framework.

    **What interviewers are really testing:** N/A (this is prep advice, not a question — but interviewers DO check if candidates have a structured problem-solving approach).

    **Follow-up:**

    * "Walk me through how you would approach a problem you have never seen before."
    * "Which Java collections are most useful for solving coding problems, and why?"
  </Accordion>

  <Accordion title="Most asked Java interview questions">
    **A ranked list of what you WILL be asked, based on frequency across hundreds of real interviews:**

    1. **HashMap internals** — How `put()` and `get()` work, bucket collisions, the linked-list-to-tree threshold. This is the single most common senior Java question.
    2. **`equals()` and `hashCode()` contract** — What breaks when they are inconsistent. Write a correct implementation.
    3. **`synchronized` vs `volatile` vs `AtomicInteger`** — Different tools for different concurrency problems.
    4. **JVM memory model** — Heap vs stack, Young Gen vs Old Gen, GC algorithms.
    5. **OOP principles with real examples** — Not definitions. "Tell me about a time you used polymorphism to solve a real problem."
    6. **Lambda expressions and Streams** — Write a stream pipeline that groups, filters, and transforms. Know `flatMap`.
    7. **Exception handling best practices** — Checked vs unchecked, try-with-resources, custom exceptions.
    8. **Spring DI and `@Transactional`** — Constructor injection, the proxy self-invocation trap.
    9. **Thread pool configuration** — How `ThreadPoolExecutor` works, core vs max pool size, the task queue.
    10. **Immutability** — How to create immutable objects, why `String` is immutable, records.

    **What interviewers are really testing:** Varies by question, but the meta-skill is: can you explain complex topics clearly and go deeper when probed?

    **Follow-up:**

    * "Pick any one of these topics and explain it to me as if I know nothing about Java."
  </Accordion>

  <Accordion title="Tips for Java system design interviews">
    **What a strong candidate knows:**

    * **Thread pool design:** `ThreadPoolExecutor` has core pool size (threads created even when idle), max pool size (upper bound), keep-alive time (how long excess threads live), and a work queue (bounded vs unbounded). Unbounded queues (`LinkedBlockingQueue`) are dangerous — they let the queue grow until OOM. Use bounded queues with rejection policies (`CallerRunsPolicy` provides backpressure).
    * **Caching layers:** L1 (in-process, Caffeine — sub-microsecond), L2 (distributed, Redis — sub-millisecond). Use Caffeine for hot data with bounded size and TTL. Use Redis when multiple service instances need shared cache state. Spring's `@Cacheable` abstracts both.
    * **Connection pooling:** HikariCP for JDBC, Apache HttpClient PoolingHttpClientConnectionManager for HTTP. Key metrics to monitor: active connections, idle connections, wait time. Connection leaks (not returning connections to pool) cause pool exhaustion — set `leakDetectionThreshold` in HikariCP.
    * **Microservices patterns in Spring:**
      * **Service discovery:** Spring Cloud + Eureka or Kubernetes DNS.
      * **Circuit breaker:** Resilience4j (successor to Hystrix). Prevents cascade failures.
      * **API gateway:** Spring Cloud Gateway for routing, rate limiting, authentication.
      * **Distributed tracing:** Micrometer Tracing with Zipkin or Jaeger.
    * **Event-driven architecture:** Spring + Kafka. Use `@KafkaListener` for consumers, `KafkaTemplate` for producers. Understand consumer groups, partition assignment, exactly-once semantics, and dead letter topics for failed message handling.

    **What interviewers are really testing:** Whether you can design systems beyond CRUD. Thread pool sizing, caching strategy, and resilience patterns separate mid-level from senior engineers.

    **Red flag answer:** "Use microservices with Spring Boot" — no specifics on how you would configure thread pools, caching, or handle failures.

    **Follow-up:**

    * "How would you size a `ThreadPoolExecutor` for an IO-bound vs CPU-bound workload?"
    * "Design a caching strategy for a read-heavy service with 100K requests/sec. What cache levels do you use?"
    * "How does a circuit breaker work, and when would you use one?"
  </Accordion>
</AccordionGroup>

<hr />

## 11. Strings and Immutability

<AccordionGroup>
  <Accordion title="Why is String immutable in Java?">
    `String` is immutable by design — once created, its character content cannot be changed. Any operation that appears to modify a String (like `concat()`, `replace()`, `toUpperCase()`) creates a new String object.

    **What a strong candidate explains:**

    * **Security:** Strings are used for class loading, network connections, database URLs, file paths, and cryptographic keys. If Strings were mutable, code that receives a validated file path could have that path changed after validation but before use (a TOCTOU vulnerability). Immutability eliminates this entire class of security bugs.
    * **Thread safety:** Immutable objects are inherently thread-safe — no synchronization needed. Strings are shared across threads constantly (HTTP headers, configuration values, cache keys). If they were mutable, every String access would need synchronization.
    * **String Pool optimization:** Because Strings are immutable, the JVM can safely deduplicate them via the String Pool (interning). Literal strings like `"hello"` are stored once in the pool and shared by all references. This saves significant memory — in a typical application, 25-40% of heap is String objects.
    * **Caching hashCode:** `String.hashCode()` is computed once and cached (lazy initialization). Since the content cannot change, the hash never changes. This makes Strings extremely efficient as `HashMap` keys. Without immutability, the cached hash would become stale after mutation.
    * **`StringBuilder` vs `StringBuffer`:** For mutable string operations, use `StringBuilder` (not thread-safe, faster) or `StringBuffer` (thread-safe, slower). In practice, always use `StringBuilder` — `StringBuffer` synchronization is almost never needed and was a pre-Java-5 design decision.

    **What interviewers are really testing:** Whether you can articulate multiple concrete reasons beyond "security" and connect immutability to HashMap efficiency, thread safety, and memory optimization.

    **Red flag answer:** "Strings are immutable for security" — just one reason, no mention of hashCode caching, thread safety, or String Pool.

    **Follow-up:**

    * "What is the String Pool? Where does it live in memory?"
    * "How does `String.hashCode()` caching improve HashMap performance?"
    * "When would you use `StringBuilder` vs `StringBuffer`? Is `StringBuffer` ever actually needed?"
  </Accordion>
</AccordionGroup>

<hr />

## 12. Java Design Patterns

<AccordionGroup>
  <Accordion title="Explain Singleton pattern and its pitfalls in Java">
    Singleton ensures only one instance of a class exists globally. In Java, it is trickier to implement correctly than people think.

    **What a strong candidate explains:**

    * **The naive approach fails:** `public static Singleton instance;` with a lazy `if (instance == null)` check is not thread-safe. Two threads can both see `null` and create separate instances.
    * **Double-checked locking (correct version):**

    ```java theme={null}
    public class Singleton {
        private static volatile Singleton instance;
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    ```

    The `volatile` keyword is **critical** — without it, the JVM might reorder the write to `instance` before the constructor finishes, letting another thread see a partially-constructed object.

    * **Enum singleton (Effective Java recommendation):** `enum Singleton { INSTANCE; }` — thread-safe, serialization-safe, reflection-safe (enum constructors cannot be called via reflection). This is the simplest correct implementation.
    * **Spring's approach:** Spring beans are singletons by default (within the container scope). You rarely need to implement the Singleton pattern manually — Spring manages it. But `@Scope("singleton")` in Spring is per-container, not per-JVM. Multiple Spring contexts means multiple "singletons."
    * **When Singleton is an anti-pattern:** It introduces global state, makes unit testing harder (cannot substitute the instance), and hides dependencies. In modern Java with DI frameworks, manual singletons are almost always a code smell. Let the framework manage instance lifecycle.

    **What interviewers are really testing:** Whether you know the thread-safety pitfalls, the volatile requirement in DCL, and whether you reflexively reach for Singleton or prefer DI.

    **Red flag answer:** A non-thread-safe implementation, or advocating for Singleton without acknowledging its testability problems.

    **Follow-up:**

    * "Why is the `volatile` keyword required in double-checked locking? What happens without it?"
    * "How does Spring manage singleton scope, and how is it different from the GoF Singleton pattern?"
    * "When is Singleton appropriate vs when is it a code smell?"
  </Accordion>

  <Accordion title="What is the Builder pattern and when should you use it?">
    Builder separates object construction from its representation, enabling the creation of complex objects step by step.

    **What a strong candidate explains:**

    * **The problem it solves:** Telescoping constructors — when a class has many optional fields, you end up with constructors like `new User(name, null, null, email, null, true, null)`. This is error-prone and unreadable.
    * **Implementation:**

    ```java theme={null}
    User user = User.builder()
        .name("Alice")
        .email("alice@example.com")
        .age(30)
        .active(true)
        .build();
    ```

    * **Lombok `@Builder`:** Generates the entire Builder pattern with a single annotation. Used in 90%+ of Spring Boot projects. Saves hundreds of lines of boilerplate. Can be combined with `@AllArgsConstructor(access = AccessLevel.PRIVATE)` to enforce Builder-only construction.
    * **When to use Builder:** (1) Classes with more than 3-4 constructor parameters, (2) Classes with many optional fields, (3) Immutable objects with complex construction, (4) Creating test fixtures with readable setup code.
    * **Builder vs Constructor vs Factory:**
      * **Constructor:** Fine for 1-3 required parameters.
      * **Factory method:** When you need to return different subtypes or cached instances.
      * **Builder:** When construction is complex, has many optional steps, or readability of the construction site matters.

    **What interviewers are really testing:** Whether you know when the Builder pattern adds value vs when it is over-engineering.

    **Red flag answer:** Describing the pattern without mentioning Lombok, or using Builder for a class with two fields.

    **Follow-up:**

    * "How does Lombok's `@Builder` work under the hood?"
    * "How would you make a Builder that validates required fields at build time?"
  </Accordion>
</AccordionGroup>

<hr />

## 13. Generics and Type System

<AccordionGroup>
  <Accordion title="Explain generics and type erasure in Java">
    Generics enable type-safe, reusable code by parameterizing classes, interfaces, and methods with type variables. At compile time, Java enforces type constraints; at runtime, generic type information is erased.

    **What a strong candidate explains:**

    * **Type erasure:** The compiler removes all generic type information after type checking. `List<String>` and `List<Integer>` are both just `List` at runtime. This means: (1) You cannot do `new T()`, (2) You cannot do `instanceof List<String>`, (3) You cannot create generic arrays `new T[]`. This is Java's backward compatibility trade-off — generics were added in Java 5 but had to interoperate with Java 1.4 bytecode.
    * **Bounded type parameters:**
      * `<T extends Comparable<T>>` — T must implement Comparable. Used in sorting algorithms.
      * `<T extends Number>` — T must be Number or a subclass.
      * Multiple bounds: `<T extends Comparable<T> & Serializable>` — T must satisfy both.
    * **Wildcards (PECS — Producer Extends, Consumer Super):**
      * `List<? extends Number>` — read items as Number, but cannot add (except null). It is a "producer" of Numbers.
      * `List<? super Integer>` — can add Integers, but read items only as Object. It is a "consumer" of Integers.
      * `List<?>` — unbounded wildcard, read-only.
    * **PECS in practice:** `Collections.copy(List<? super T> dest, List<? extends T> src)` — the source produces T values, the destination consumes them. This guideline is from Effective Java and is the key to writing correct generic APIs.
    * **Reified generics in other JVM languages:** Kotlin has `reified` type parameters (via `inline` functions) that retain type info at runtime. Scala has `ClassTag`/`TypeTag`. Java may get this eventually (Project Valhalla).

    **What interviewers are really testing:** Whether you understand type erasure and PECS, which separate mid-level from senior Java developers.

    **Red flag answer:** "Generics let you use type parameters like `List<String>`" — no mention of type erasure, wildcards, or PECS.

    **Follow-up:**

    * "Why can't you write `new T()` or `instanceof List<String>` in Java?"
    * "Explain PECS with a real example. When would you use `? extends` vs `? super`?"
    * "How does type erasure affect serialization/deserialization of generic types (e.g., Jackson)?"
  </Accordion>
</AccordionGroup>

<hr />

## 14. Modern Java Features (Java 11-21)

<AccordionGroup>
  <Accordion title="What new features in Java 11-21 should every developer know?">
    Java's six-month release cadence since Java 9 has introduced significant features. Knowing modern Java separates a current developer from someone stuck on Java 8.

    **Key features by version:**

    * **Java 11 (LTS):** `var` for local type inference, `String` new methods (`isBlank()`, `strip()`, `lines()`, `repeat()`), HTTP Client API (`java.net.http`), single-file source execution (`java FileName.java`).
    * **Java 14:** Switch expressions (`->` syntax, no fall-through), `NullPointerException` with helpful messages showing exactly which variable was null.
    * **Java 16:** Records (`record Point(int x, int y) {}` — immutable data carriers with auto-generated `equals()`, `hashCode()`, `toString()`), Pattern matching for `instanceof` (`if (obj instanceof String s)` — no cast needed).
    * **Java 17 (LTS):** Sealed classes (`sealed class Shape permits Circle, Square` — restrict which classes can extend). This enables exhaustive pattern matching in switch.
    * **Java 21 (LTS):** **Virtual threads** (lightweight threads, millions per JVM), pattern matching for switch, sequenced collections (`SequencedCollection`, `SequencedMap` with `getFirst()`, `getLast()`), record patterns.

    **What a strong candidate highlights:**

    * **Virtual threads are the biggest change since lambdas.** They eliminate the need for reactive programming (WebFlux, RxJava) in most IO-bound applications. Instead of 200 platform threads handling 10,000 concurrent requests with async callbacks, you create 10,000 virtual threads — one per request — with simple blocking code. The JVM multiplexes them onto a small pool of platform threads.
    * **Records replace 80% of Lombok usage** for data carrier classes. They enforce immutability, generate `equals()`/`hashCode()`/`toString()`, and work well with pattern matching.
    * **Sealed classes + pattern matching** enable algebraic data types in Java — similar to Kotlin's `sealed class` or Rust's enums. The compiler can verify exhaustiveness in switch expressions.

    **What interviewers are really testing:** Whether you are current with the language. Saying "I've only used Java 8" in 2025 is a yellow flag.

    **Red flag answer:** Not knowing about records, virtual threads, or any feature after Java 8.

    **Follow-up:**

    * "How do virtual threads work? What is the relationship to platform threads?"
    * "When would you use a record vs a regular class?"
    * "How do sealed classes enable exhaustive pattern matching?"
  </Accordion>

  <Accordion title="What are Java Records and when should you use them?">
    Records (Java 16+) are a special class form for immutable data carriers. `record User(String name, int age) {}` auto-generates: a canonical constructor, `final` fields, accessor methods (`name()`, `age()`), `equals()`, `hashCode()`, and `toString()`.

    **What a strong candidate explains:**

    * **What records replace:** Boilerplate data classes that are 50+ lines for 3 fields (fields, constructor, getters, equals, hashCode, toString). Records reduce this to one line. They also replace most Lombok `@Value` and `@Data` usage.
    * **Restrictions by design:** Records cannot extend other classes (they implicitly extend `Record`). Fields are final — no setters. You cannot add instance fields outside the record header. These constraints are features: they guarantee immutability and value-based equality.
    * **Custom behavior allowed:** You can add custom constructors (compact constructors for validation), instance methods, static methods, and implement interfaces:

    ```java theme={null}
    record Email(String value) {
        Email { // compact constructor
            if (!value.contains("@"))
                throw new IllegalArgumentException("Invalid email");
        }
    }
    ```

    * **Where records shine:** DTOs, API response/request objects, value objects in domain-driven design, pattern matching, keys in Maps. Spring supports records for `@RequestBody` deserialization, `@ConfigurationProperties`, and more.
    * **Where records do NOT fit:** JPA entities (entities need mutable state, no-arg constructor, and are identity-based, not value-based), objects with complex mutable lifecycle, classes that need inheritance.

    **What interviewers are really testing:** Whether you understand the trade-offs and know when records are appropriate vs when a regular class is still needed.

    **Red flag answer:** "Records are like Lombok's `@Data`" — they are closer to `@Value` (immutable), and this conflation shows misunderstanding of the immutability constraint.

    **Follow-up:**

    * "Can you use records as JPA entities? Why or why not?"
    * "How do records interact with pattern matching in Java 21?"
    * "What is a compact constructor, and when would you use one?"
  </Accordion>
</AccordionGroup>

<hr />

## Conclusion & Interview Tips

### Key Focus Areas

* OOP concepts and Java 8+ features (lambdas, streams, Optional)
* Multithreading, collections internals (especially HashMap), and memory management
* Spring Boot fundamentals, dependency injection, and the `@Transactional` proxy trap
* Clean code, exception handling, and immutability patterns
* Modern Java (records, virtual threads, sealed classes, pattern matching)

### During the Interview

* Start with a crisp one-liner answer, then go deeper layer by layer
* Discuss time and space complexity with specific Big-O for collection operations
* Always mention trade-offs — "it depends" is fine IF you explain what it depends on
* Use real examples: "In a service handling 10K requests/sec, I would..."
* Show debugging instincts: "The first thing I would check is..."
* Know your tools: mention profilers (async-profiler, JFR), monitoring (Micrometer, Actuator), and build tools (Maven, Gradle)

### Red Flags Interviewers Watch For

* Textbook definitions with zero real-world context
* "Use synchronized everywhere" without knowing alternatives
* Not knowing HashMap internals at a senior level
* Field injection preference over constructor injection
* No awareness of Java features after Java 8
* Unable to explain WHY, only WHAT

<Info>
  Java interviews at the senior level emphasize internals (JVM memory, HashMap buckets, thread pools), production experience (GC tuning, memory leaks, deadlocks), and design judgment (when to use what, trade-offs). Do not just know the concepts — know when they break, why they break, and how to fix them in production.
</Info>

<Check>
  Good luck with your Java Developer interviews!
</Check>
