Skip to content
Java inner classes 5 min read

Member Inner Class

A member inner class is a non-static class declared directly inside another class — at the same level as fields and methods. It has a special privilege: it can freely access all members of its enclosing (outer) class, including private ones.

What Is a Member Inner Class?

When you define a class inside another class without the static keyword, you get a member inner class (sometimes just called an “inner class”). Think of it as a class that belongs to an instance of the outer class rather than to the outer class itself.

This relationship is what makes it powerful: every instance of the inner class holds an invisible reference to the outer instance that created it.

class Outer {
    private int value = 42;

    class Inner {
        void display() {
            // Inner can directly access outer's private field
            System.out.println("Outer value: " + value);
        }
    }
}

Creating an Instance

Because a member inner class belongs to an outer instance, you must have an outer object first. There are two ways to instantiate it.

From outside the outer class:

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();           // 1. create outer instance
        Outer.Inner inner = outer.new Inner(); // 2. create inner via outer
        inner.display();
    }
}

Output:

Outer value: 42

From inside the outer class:

Inside the outer class, you can use new Inner() directly — no qualifier needed.

class Outer {
    private int value = 42;

    class Inner {
        void display() {
            System.out.println("Outer value: " + value);
        }
    }

    void createInner() {
        Inner inner = new Inner(); // no qualifier required here
        inner.display();
    }
}

Note: The syntax outer.new Inner() looks unusual at first, but it is intentional — it creates an Inner instance tied to the specific outer object.

Accessing Outer Members

A member inner class can access every member of the enclosing class: public, protected, package-private, and private. This is the primary reason to use a member inner class over a standalone helper class.

class BankAccount {
    private double balance = 1000.0;
    private String owner = "Alice";

    class Statement {
        void print() {
            // Accessing two private fields of BankAccount
            System.out.println("Account owner : " + owner);
            System.out.println("Current balance: $" + balance);
        }
    }

    void printStatement() {
        new Statement().print();
    }
}

public class Main {
    public static void main(String[] args) {
        BankAccount account = new BankAccount();
        account.printStatement();
    }
}

Output:

Account owner : Alice
Current balance: $1000.0

Shadowing and this Disambiguation

If the inner class declares a field or parameter with the same name as an outer field, the inner name shadows the outer one. Use the qualified OuterClass.this syntax to reach the outer version.

class Outer {
    int x = 10;

    class Inner {
        int x = 20; // shadows Outer.x

        void show() {
            System.out.println("Inner x        : " + x);           // 20
            System.out.println("Outer x        : " + Outer.this.x); // 10
        }
    }
}

public class Main {
    public static void main(String[] args) {
        new Outer().new Inner().show();
    }
}

Output:

Inner x        : 20
Outer x        : 10

See this Keyword for more on how this works in regular classes.

Inner Classes and Inheritance

A member inner class can extend another class or implement interfaces just like any regular class. It is often used to implement helper behavior that is tightly coupled to the outer class.

class Graph {
    interface Drawable {
        void draw();
    }

    class Node implements Drawable {
        private String label;

        Node(String label) {
            this.label = label;
        }

        @Override
        public void draw() {
            System.out.println("Drawing node: " + label);
        }
    }

    void render() {
        Node node = new Node("A");
        node.draw();
    }
}

Tip: If your inner class does not need to access outer instance state, prefer a static nested class. It is lighter and avoids holding the outer reference.

Member Inner Class vs Static Nested Class

FeatureMember Inner ClassStatic Nested Class
Needs outer instance?YesNo
Access outer instance members?Yes (including private)No (only static members)
Holds implicit outer reference?YesNo
Risk of memory leak?Yes (outer kept alive)No
Typical use caseTightly coupled helperLoosely coupled helper / builder

Read the static nested class page for a side-by-side comparison.

Under the Hood

When the Java compiler encounters a member inner class, it generates a separate top-level .class file named Outer$Inner.class. Inside that file the compiler inserts a hidden synthetic field — conventionally named this$0 — that stores a reference to the enclosing Outer instance.

// Roughly what the compiler produces for Inner
class Outer$Inner {
    final Outer this$0;  // synthetic reference injected by compiler

    Outer$Inner(Outer outer) {
        this.this$0 = outer;
    }

    void display() {
        System.out.println("Outer value: " + this$0.value);
    }
}

Because every Inner object holds a hard reference to its Outer object, the outer instance cannot be garbage-collected as long as any inner instance is reachable. In long-lived scenarios (listeners, callbacks stored in collections) this can cause subtle memory leaks — the outer object stays in the heap even after you think it is no longer needed.

You can inspect the generated bytecode with the javap tool:

javac Outer.java
javap -p Outer\$Inner

You will see the synthetic this$0 field in the output.

Warning: Storing an inner-class instance inside a static field, a singleton, or a long-lived collection keeps the entire outer object alive. If that is a problem, switch to a static nested class or extract the class to the top level.

Quick Rules to Remember

  • A member inner class is declared inside a class body, without static.
  • Instantiation always requires an outer instance: outer.new Inner().
  • It can access all outer members (any access level) directly.
  • Use OuterClass.this.field to disambiguate shadowed names.
  • The compiler creates Outer$Inner.class with a synthetic this$0 field.
  • Non-static inner classes can cause memory leaks — keep the relationship short-lived, or switch to a static nested class.
Last updated June 13, 2026
Was this helpful?