Q: Explain the four kinds of nested classes in Java.

Answer:

TypeStatic?Holds outer ref?Use case
Static nestedyesnoLogical grouping; helper attached to enclosing class
Inner (member)noyesTightly bound to outer instance
Localnoyes (if in instance method)One-method-only helper
AnonymousnoyesOne-shot interface/abstract impl

1. Static Nested

class Outer {
    static class Builder {
        Outer build() { return new Outer(); }
    }
}
Outer o = new Outer.Builder().build();

No outer instance needed. Same as a top-level class but namespaced.

2. Inner (Member) Class

class Outer {
    private int x = 10;
    class Inner {
        int read() { return x; }   // implicit Outer.this reference
    }
}
Outer.Inner i = new Outer().new Inner();  // needs outer instance

[!WARNING] Inner classes hold a hidden reference to the outer instance — common cause of memory leaks (e.g., non-static Handler holding Activity in Android, or non-static inner classes referenced by long-lived collections).

3. Local Class (Inside Method)

void process(List<String> items) {
    class LengthFilter {
        boolean keep(String s) { return s.length() > 3; }
    }
    LengthFilter f = new LengthFilter();
    items.removeIf(s -> !f.keep(s));
}

Captures effectively final local variables.

4. Anonymous Class

Runnable r = new Runnable() {
    @Override public void run() { System.out.println("hi"); }
};

Mostly replaced by lambdas (Java 8+) for single-method interfaces:

Runnable r = () -> System.out.println("hi");

Lambdas vs Anonymous Classes

LambdaAnonymous class
this refers toenclosing classthe anonymous instance
Compiled toinvokedynamic / synthetic methodnew .class file
Can hold statenoyes (instance fields)
Multiple methodsno (single abstract method only)yes

Effectively Final Capture

void demo() {
    int x = 10;
    Runnable r = () -> System.out.println(x);  // ✅ x not reassigned
    // x = 20; ← would break the lambda
}

Memory Leak Example

class Repository {
    private List<Listener> listeners = new ArrayList<>();
    void register() {
        listeners.add(new Listener() {     // anonymous → holds Repository.this
            public void onEvent() { ... }
        });
    }
}
// Listener pinned in `listeners` → Repository instance never GC'd if list outlives it.

Fix: make it static nested, or store no reference, or use weak refs.