AI InterviewSearch…Search problems, topics, or pages…Ctrl KGet started
Skip to content

11 Nested classes & lambdas / Lesson 1 of 5

Static nested and inner classes

13 min · +10 XP · 2 exercises · 5 questions

Read the text13 min
Exercises0/2
Quiz5 q

Quick answer

What is the difference between a static nested class and an inner class in Java?

A static nested class is declared inside another class with the static modifier; it has no link to an outer instance and can be created on its own. An inner class, a non-static member class, carries a hidden reference, Outer.this, to the outer instance it was created from, so it reads that instance's fields directly — and keeps it alive.

A class can be declared inside another class. Java has four kinds of such nested classes — static nested, inner (non-static member), local and anonymous — and the first two differ in one crucial way: an inner class instance secretly holds a reference to an instance of the outer class; a static nested class does not. That reference is convenient, and it is also the cause of memory leaks and confusing this rules. This lesson makes the distinction sharp and gives the rule for which to use.

Static nested classes

public class LinkedStack<T> {
    private static class Node<T> {            // static nested: a helper type in the outer class's namespace
        final T value;
        final Node<T> next;
        Node(T value, Node<T> next) { this.value = value; this.next = next; }
    }

    private Node<T> head;

    public void push(T v) { head = new Node<>(v, head); }
    public T pop() { T v = head.value; head = head.next; return v; }
}

A static nested class is an ordinary class that happens to live inside another, for scoping and packaging. It has no reference to an outer instance; it can be created without one (new LinkedStack.Node<>(…) from outside, if accessible, or just new Node<>(…) inside). It can access the outer class's static members and — because nesting is a trust relationship — the outer class's private static members. The outer class can access the nested class's private members too.

Use it for: helper types that belong to one class (Map.Entry, HashMap.Node, builders — Pizza.Builder), and for grouping small related types. This should be your default kind of nested class.

Inner classes (non-static member classes)

public class Outer {
    private int count = 0;

    public class Counter {                    // inner: every Counter belongs to an Outer instance
        public void increment() { count++; }   // reads the OUTER instance's field directly
        public Outer owner() { return Outer.this; }
    }

    public Counter counter() { return new Counter(); }    // created inside an instance method: implicit outer = this
}

Outer o = new Outer();
Outer.Counter c = o.counter();
Outer.Counter c2 = o.new Counter();           // explicit outer instance — rarely seen, but this is the syntax
c.increment();                                // o.count is now 1

An inner class instance carries a hidden field, Outer.this, pointing at the outer instance it was created from. Inside it, unqualified names resolve outward: count means Outer.this.count. this alone is the inner instance; Outer.this is the outer.

Consequences:

  • Cannot be created without an outer instance (new Counter() from a static method fails: "an enclosing instance that contains Outer.Counter is required").
  • Before Java 16, inner classes could not declare static members (except constants); since Java 16 they can.
  • Every inner instance keeps its outer instance alive. A Counter stored in a long-lived collection prevents its Outer from being garbage-collected — the classic Java memory leak, historically in Android with inner-class listeners holding Activitys.

Use it when the inner object genuinely is part of the outer's state and needs its fields: an Iterator implementation inside a collection (ArrayList.Itr is an inner class reading ArrayList's array and modCount), an event-handler that manipulates the enclosing component.

The rule

Effective Java item 24: if a nested class does not need access to the enclosing instance, make it static. Every non-static nested class costs a hidden field, a hidden constructor parameter, and a lifetime dependency. IDEs warn "inner class may be static" for exactly this reason. Start with static; remove it only when you need Outer.this.

Nested types of other kinds

  • Nested interfaces, enums and records are implicitly static — they can never be inner classes.
  • A nested class inside an interface is implicitly public static.
  • Nesting depth is unlimited; more than two levels is a design smell.

Access, visibility and naming

Nested classes may use all four access levels (top-level classes only two). A private nested class is visible only within the top-level class that contains it — including to sibling nested classes. From outside, the name is Outer.Nested; the compiled class file is Outer$Nested.class, which you will see in stack traces and javap output. Anonymous classes compile to Outer$1.class, Outer$2.class.

Shadowing outward

public class Outer {
    int x = 1;
    class Inner {
        int x = 2;
        void show(int x) {
            System.out.println(x);            // 3: the parameter
            System.out.println(this.x);       // 2: Inner's field
            System.out.println(Outer.this.x); // 1: Outer's field
        }
    }
}

Names resolve innermost first; qualify to reach outer levels. Avoid shadowing across nesting levels — it is legal and unreadable.

Inner classes and inheritance

An inner class can extend another class and be extended; a subclass of an inner class must also have an outer instance, which makes such hierarchies awkward. Interfaces implemented by inner classes (the Iterator case) are the common form; inheritance among inner classes is rare and should stay rare.

Interview angle

  • "Difference between a static nested class and an inner class?" An inner class instance holds a reference to an enclosing instance and can use its fields; a static nested class does not and can be created without one.
  • "How do you create an inner class instance from outside?" outer.new Inner().
  • "Why can inner classes cause memory leaks?" The hidden Outer.this reference keeps the outer object reachable.
  • "Which should you prefer?" Static nested, unless the enclosing instance is genuinely needed.
  • "Can an inner class have static members?" Since Java 16, yes; before, only compile-time constants.

Key takeaways

  • Static nested: a class in another's namespace, no outer instance — the default choice.
  • Inner: carries Outer.this, reads outer fields directly, needs an outer instance, keeps it alive.
  • Nested interfaces/enums/records are always static; nested classes may be private.
  • Outer.this.x reaches the outer field when names shadow; outer.new Inner() creates with an explicit outer.

Common questions

How do you create an instance of an inner class in Java?

Inside an instance method of the outer class, new Inner() uses this as the enclosing instance. From outside, qualify it with an outer object: outer.new Inner(). From a static method with no outer instance the compiler refuses: "an enclosing instance that contains Outer.Inner is required".

Why can inner classes cause memory leaks in Java?

Every inner class instance holds a hidden Outer.this reference, so while the inner object is reachable its outer object cannot be garbage-collected. An inner-class listener stored in a long-lived collection is the classic case — on Android it kept whole Activity objects alive.

Should a nested class be static or non-static?

Static, unless it needs the enclosing instance. Effective Java (item 24) gives the rule: if a nested class does not need access to the enclosing instance, make it static. A non-static one costs a hidden field, a hidden constructor parameter and a lifetime dependency on its outer object.

Can an inner class have static members in Java?

Since Java 16, yes. Before Java 16 an inner class could declare only compile-time constants as static members. Nested interfaces, enums and records are implicitly static, so they can never be inner classes.

How do you access an outer class field from an inner class?

Unqualified names resolve outward, so count inside the inner class reads the outer instance's field when nothing shadows it. When an inner field or parameter has the same name, Outer.this.x reaches the outer field and this.x the inner one.

Mark as readSign in to keep your place.

Static Nested Class vs Inner Class in Java Explained

An inner class holds a hidden reference to its outer instance; a static nested class does not. When to use each, Outer.this, and the inner-class memory leak.

  • Course: Java study plan
  • Module: Nested classes & lambdas
  • Kind: Lesson
  • Reading time: 13 min
  • Runtime: OpenJDK 18

What is the difference between a static nested class and an inner class in Java?

A static nested class is declared inside another class with the static modifier; it has no link to an outer instance and can be created on its own. An inner class, a non-static member class, carries a hidden reference, Outer.this, to the outer instance it was created from, so it reads that instance's fields directly — and keeps it alive.

Lesson

A class can be declared inside another class. Java has four kinds of such nested classes — static nested, inner (non-static member), local and anonymous — and the first two differ in one crucial way: an inner class instance secretly holds a reference to an instance of the outer class; a static nested class does not. That reference is convenient, and it is also the cause of memory leaks and confusing this rules. This lesson makes the distinction sharp and gives the rule for which to use.

Static nested classes

public class LinkedStack<T> {
    private static class Node<T> {            // static nested: a helper type in the outer class's namespace
        final T value;
        final Node<T> next;
        Node(T value, Node<T> next) { this.value = value; this.next = next; }
    }

    private Node<T> head;

    public void push(T v) { head = new Node<>(v, head); }
    public T pop() { T v = head.value; head = head.next; return v; }
}

A static nested class is an ordinary class that happens to live inside another, for scoping and packaging. It has no reference to an outer instance; it can be created without one (new LinkedStack.Node<>(…) from outside, if accessible, or just new Node<>(…) inside). It can access the outer class's static members and — because nesting is a trust relationship — the outer class's private static members. The outer class can access the nested class's private members too.

Use it for: helper types that belong to one class (Map.Entry, HashMap.Node, builders — Pizza.Builder), and for grouping small related types. This should be your default kind of nested class.

Inner classes (non-static member classes)

public class Outer {
    private int count = 0;

    public class Counter {                    // inner: every Counter belongs to an Outer instance
        public void increment() { count++; }   // reads the OUTER instance's field directly
        public Outer owner() { return Outer.this; }
    }

    public Counter counter() { return new Counter(); }    // created inside an instance method: implicit outer = this
}

Outer o = new Outer();
Outer.Counter c = o.counter();
Outer.Counter c2 = o.new Counter();           // explicit outer instance — rarely seen, but this is the syntax
c.increment();                                // o.count is now 1

An inner class instance carries a hidden field, Outer.this, pointing at the outer instance it was created from. Inside it, unqualified names resolve outward: count means Outer.this.count. this alone is the inner instance; Outer.this is the outer.

Consequences:

  • Cannot be created without an outer instance (new Counter() from a static method fails: "an enclosing instance that contains Outer.Counter is required").
  • Before Java 16, inner classes could not declare static members (except constants); since Java 16 they can.
  • Every inner instance keeps its outer instance alive. A Counter stored in a long-lived collection prevents its Outer from being garbage-collected — the classic Java memory leak, historically in Android with inner-class listeners holding Activitys.

Use it when the inner object genuinely is part of the outer's state and needs its fields: an Iterator implementation inside a collection (ArrayList.Itr is an inner class reading ArrayList's array and modCount), an event-handler that manipulates the enclosing component.

The rule

Effective Java item 24: if a nested class does not need access to the enclosing instance, make it static. Every non-static nested class costs a hidden field, a hidden constructor parameter, and a lifetime dependency. IDEs warn "inner class may be static" for exactly this reason. Start with static; remove it only when you need Outer.this.

Nested types of other kinds

  • Nested interfaces, enums and records are implicitly static — they can never be inner classes.
  • A nested class inside an interface is implicitly public static.
  • Nesting depth is unlimited; more than two levels is a design smell.

Access, visibility and naming

Nested classes may use all four access levels (top-level classes only two). A private nested class is visible only within the top-level class that contains it — including to sibling nested classes. From outside, the name is Outer.Nested; the compiled class file is Outer$Nested.class, which you will see in stack traces and javap output. Anonymous classes compile to Outer$1.class, Outer$2.class.

Shadowing outward

public class Outer {
    int x = 1;
    class Inner {
        int x = 2;
        void show(int x) {
            System.out.println(x);            // 3: the parameter
            System.out.println(this.x);       // 2: Inner's field
            System.out.println(Outer.this.x); // 1: Outer's field
        }
    }
}

Names resolve innermost first; qualify to reach outer levels. Avoid shadowing across nesting levels — it is legal and unreadable.

Inner classes and inheritance

An inner class can extend another class and be extended; a subclass of an inner class must also have an outer instance, which makes such hierarchies awkward. Interfaces implemented by inner classes (the Iterator case) are the common form; inheritance among inner classes is rare and should stay rare.

Interview angle

  • "Difference between a static nested class and an inner class?" An inner class instance holds a reference to an enclosing instance and can use its fields; a static nested class does not and can be created without one.
  • "How do you create an inner class instance from outside?" outer.new Inner().
  • "Why can inner classes cause memory leaks?" The hidden Outer.this reference keeps the outer object reachable.
  • "Which should you prefer?" Static nested, unless the enclosing instance is genuinely needed.
  • "Can an inner class have static members?" Since Java 16, yes; before, only compile-time constants.

Key takeaways

  • Static nested: a class in another's namespace, no outer instance — the default choice.
  • Inner: carries Outer.this, reads outer fields directly, needs an outer instance, keeps it alive.
  • Nested interfaces/enums/records are always static; nested classes may be private.
  • Outer.this.x reaches the outer field when names shadow; outer.new Inner() creates with an explicit outer.

Common questions

How do you create an instance of an inner class in Java?

Inside an instance method of the outer class, new Inner() uses this as the enclosing instance. From outside, qualify it with an outer object: outer.new Inner(). From a static method with no outer instance the compiler refuses: "an enclosing instance that contains Outer.Inner is required".

Why can inner classes cause memory leaks in Java?

Every inner class instance holds a hidden Outer.this reference, so while the inner object is reachable its outer object cannot be garbage-collected. An inner-class listener stored in a long-lived collection is the classic case — on Android it kept whole Activity objects alive.

Should a nested class be static or non-static?

Static, unless it needs the enclosing instance. Effective Java (item 24) gives the rule: if a nested class does not need access to the enclosing instance, make it static. A non-static one costs a hidden field, a hidden constructor parameter and a lifetime dependency on its outer object.

Can an inner class have static members in Java?

Since Java 16, yes. Before Java 16 an inner class could declare only compile-time constants as static members. Nested interfaces, enums and records are implicitly static, so they can never be inner classes.

How do you access an outer class field from an inner class?

Unqualified names resolve outward, so count inside the inner class reads the outer instance's field when nothing shadows it. When an inner field or parameter has the same name, Outer.this.x reaches the outer field and this.x the inner one.

Exercises

A list with an inner iterator

Write class IntList backed by a growing int[] with add(int) and size(), a static nested class Stats (fields min, max, sum; a toString of min=<m> max=<M> sum=<s>) returned by stats(), and an inner class Cursor (created by cursor()) with hasNext()/next() that walks the list reading the outer object's array and size directly.

Read an integer n and n integers, add them, print the elements via a Cursor space-separated, then print stats(). For an empty list print an empty line and min=0 max=0 sum=0.

Example: 3 then 4 -2 9 →

4 -2 9
min=-2 max=9 sum=11

A linked list with a nested Node

Write class IntList with a private static nested class Node { int value; Node next; }, and methods addLast(int), addFirst(int), int size() and String toString() giving [a, b, c]. Read n commands first <v>, last <v> or show (prints the list and size=<n>). The node type is an implementation detail — nothing outside the list can name it.

Example: 4 then last 2, last 3, first 1, show →

[1, 2, 3] size=3

In this module: Nested classes & lambdas

← Checkpoint — Enums & records · Local and anonymous classes →