W3docs

Java Inner Classes

Define non-static inner classes in Java that hold an implicit reference to an instance of the enclosing class.

An inner class is a non-static nested class — one declared inside another class without the static modifier. The defining feature: every inner-class instance is bound to an instance of the enclosing class and carries an implicit reference to it. From inside the inner class, you can read and write the outer instance's fields and call its methods as if they were your own.

This makes inner classes the right tool when a small, secondary class needs to participate intimately in another class's state — most famously, iterators that walk over their container's internal data.

Declaring an inner class

Drop static from a nested class declaration:

public class Outer {
  private int x = 1;

  class Inner {                       // no static — inner class
    int get() { return x; }            // reads Outer's x directly
  }
}

Inner has no fields of its own and yet get() returns 1. The bare x resolves to Outer.this.x through the implicit reference.

Creating an instance

Because every inner-class instance is tied to an outer one, you need an outer instance to create an inner one. There are two ways:

Outer       o = new Outer();
Outer.Inner i = o.new Inner();         // bind explicitly to o

…or from inside a non-static method of Outer:

public class Outer {
  void demo() {
    Inner i = new Inner();             // implicitly bound to this
  }
}

The o.new Inner() syntax is rare and surprising — most code creates inner-class instances from inside the outer class's own methods, where the binding is implicit.

Outer.this — reaching past a name collision

When the inner class declares a field with the same name as one in the outer class, the inner one shadows it. To reach the outer one, qualify it with Outer.this:

public class Outer {
  int x = 1;
  class Inner {
    int x = 2;
    void demo() {
      System.out.println(x);             // 2  — Inner's x
      System.out.println(this.x);        // 2  — Inner's x
      System.out.println(Outer.this.x);  // 1  — Outer's x
    }
  }
}

this in an inner class refers to the inner instance; Outer.this refers to the enclosing outer instance.

The canonical use case — iterators

The classic reason to use an inner class is implementing an iterator over a container's private internals:

public class IntList {
  private int[] data;
  private int   size;

  // ... constructors, add, ...

  public Iterator<Integer> iterator() {
    return new InnerIterator();
  }

  private class InnerIterator implements Iterator<Integer> {
    private int i = 0;
    public boolean hasNext()  { return i < size; }
    public Integer next()     { return data[i++]; }
  }
}

InnerIterator reaches data and size directly through the implicit outer reference. No setter, no accessor needed — the inner class is part of IntList's implementation.

Note private class InnerIterator. From outside, callers see the public Iterator<Integer> interface; they don't know InnerIterator exists. That's the encapsulation benefit of nesting.

Inner classes hold a reference — so they keep the outer alive

A subtle gotcha. As long as an inner-class instance is reachable, the JVM cannot garbage-collect the outer instance it's bound to. Returning an inner-class instance to long-lived code (e.g. installing a listener somewhere) can keep entire object graphs alive longer than expected.

public class Window {
  Listener installListener() {
    return new Listener();           // returned to whoever calls this
  }
  class Listener { ... }              // holds a Window reference forever
}

If installListener() is stored in a static registry, the Window lives until the registry is cleared. The fix is usually to make the nested class static and pass any needed data explicitly, breaking the implicit reference.

This is the single most common reason teams reach for static nested classes by default and only switch to inner classes when they specifically need the binding.

Static members on inner classes

For most of Java's history, inner classes were not allowed to declare static members (static fields, methods, or nested classes). Java 16 relaxed this — inner classes can now have static members. Even so, if you find yourself wanting them, that's often a signal that the class wants to be static itself.

static vs inner — the choice

A useful rule: make it static unless you actively need the outer reference.

  • Static nested class: simpler, lighter, doesn't pin the outer alive.
  • Inner class: convenient sugar over outerInstance.field access when the relationship is genuine.

If the only thing you ever do is Outer.this.field, just take an Outer as a constructor parameter and make the class static.

A worked example

java— editable, runs on the server

What's next

The next nested-class flavor is the inline, one-shot version: anonymous classes, used for quick subclass-and-instantiate-in-one-expression situations. Continue to anonymous classes.

Practice

Practice

Why do many teams default to a static nested class even when a non-static inner class would work?