Skip to content
Java inner classes 6 min read

Inner Classes

Java lets you define a class inside another class. These inner classes (also called nested classes) are a powerful tool for grouping closely related logic, keeping implementation details hidden, and writing more expressive code — whether you’re building event listeners, iterators, or helper structures.

What Is an Inner Class?

An inner class is simply a class declared within the body of another class (the outer class). The outer class treats the inner class as a member, just like a field or a method.

class Outer {
    private int value = 42;

    class Inner {
        void display() {
            // Inner can directly access outer private member
            System.out.println("Value: " + value);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.display();
    }
}

Output:

Value: 42

The inner class has direct access to all members of the outer class — including private ones — without any getter methods.

Why Use Inner Classes?

You might wonder why Java needs this feature at all. Here are the practical reasons:

  • Logical grouping — when a class is only useful to one other class, keeping it inside makes the relationship clear.
  • Encapsulation — the inner class can be hidden from the outside world by making it private.
  • Reduced boilerplate — no need for extra files or complex interfaces for simple helper classes.
  • Access to outer state — non-static inner classes hold an implicit reference to their outer instance, so they can read and modify its state freely.

A classic real-world example is the iterator pattern: ArrayList’s iterator is implemented as a private inner class that holds a reference to the list it iterates over.

The Four Types of Inner Classes

Java has four distinct kinds of nested classes, each with its own rules and use cases:

TypeWhere declaredstatic?Can access outer instance?
Member Inner ClassInside class bodyNoYes
Local Inner ClassInside a methodNoYes (effectively final vars)
Anonymous Inner ClassInline, no nameNoYes (effectively final vars)
Static Nested ClassInside class bodyYesNo

Note: Only non-static inner classes (member, local, anonymous) hold a hidden reference to the enclosing outer instance. Static nested classes do not — they behave more like top-level classes that happen to live inside another class.

A Quick Taste of Each Type

Member Inner Class

Declared at the class level, just like a field:

class Engine {
    private String type = "V8";

    class Piston {
        void fire() {
            System.out.println("Firing piston in " + type + " engine");
        }
    }
}

See the full guide at Member Inner Class.

Anonymous Inner Class

Created inline, usually to implement an interface or extend a class on the fly:

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

Output:

Running anonymously!

Tip: In modern Java (8+), a single-method interface like Runnable is a functional interface, so you can replace anonymous classes with concise lambda expressions. But anonymous classes still shine when you need to override multiple methods or maintain local state.

Local Inner Class

Declared inside a method body, visible only within that method:

class Greeter {
    void greet(String name) {
        class Message {
            void print() {
                System.out.println("Hello, " + name + "!");
            }
        }
        new Message().print();
    }
}

Static Nested Class

Uses the static keyword; no implicit outer reference needed:

class Config {
    static class Database {
        String url = "jdbc:mysql://localhost/mydb";
    }
}

// No Config instance needed
Config.Database db = new Config.Database();
System.out.println(db.url);

Under the Hood

Understanding what the Java compiler actually does with inner classes explains several subtle behaviors.

Separate .class Files

The compiler generates a separate .class file for every inner class. A Member Inner Class declared inside Outer compiles to Outer$Inner.class. You can verify this by running javac and listing the output:

Outer.class
Outer$Inner.class

Anonymous classes get numbered names like Outer$1.class, Outer$2.class, and so on.

The Hidden this$0 Reference

For non-static inner classes, the compiler adds a hidden synthetic field (often called this$0) that holds a reference to the enclosing outer instance. This is why:

  1. You need an outer instance to instantiate a non-static inner class (outer.new Inner()).
  2. The inner class can access private members of the outer class — the compiler generates package-private synthetic access methods to bridge the visibility gap.
  3. Memory concern: If you pass an inner class instance somewhere that outlives the outer object (e.g., storing it in a static field), the outer instance cannot be garbage-collected. This is a common source of memory leaks, especially with anonymous listeners in Android or Swing. Prefer static nested classes when you don’t need the outer reference.

Effectively Final Variables

Local and anonymous inner classes can capture variables from their enclosing scope, but those variables must be effectively final (not reassigned after the initial assignment). This rule exists because the compiler copies the captured variable into the inner class — if the variable could change later, the copy would become stale and produce confusing bugs.

void process(int count) {
    // count is effectively final — never reassigned
    Runnable r = new Runnable() {
        public void run() {
            System.out.println("Count: " + count); // OK
        }
    };
    r.run();
}

Warning: Trying to reassign a captured variable inside an inner class is a compile-time error: local variables referenced from an inner class must be final or effectively final.

Choosing the Right Type

Use this quick guide when deciding which kind of nested class to reach for:

  • Member inner class — a helper that needs to read or modify the outer object’s state and is used in more than one method.
  • Anonymous inner class — a one-off implementation of an interface or abstract class; no lambda equivalent exists (multiple methods, constructor args, etc.).
  • Local inner class — complex logic needed only inside one method; too big for an anonymous class.
  • Static nested class — a helper class that is logically related to the outer class but doesn’t need access to an outer instance (e.g., a builder, a configuration record, an entry type).

Tip: When in doubt, prefer static nested classes. They are lighter — no hidden outer reference — and easier to test and reason about in isolation.

In This Section

  • Member Inner Class — A non-static class declared directly inside another class, with full access to the outer instance’s members.
  • Anonymous Inner Class — An unnamed class defined and instantiated in a single expression, perfect for quick one-off implementations.
  • Local Inner Class — A class declared inside a method body, scoped entirely to that method.
  • Static Nested Class — A static member class that has no implicit outer reference, behaving like a top-level class in a different namespace.
  • Nested Interface — An interface declared inside a class or another interface, often used to define callbacks and contracts tightly coupled to an enclosing type.
  • Classes & Objects — the foundation you need before diving into nesting classes inside one another.
  • Anonymous Inner Class — the most common inner class you’ll encounter in everyday Java code.
  • Lambda Expressions — the modern, concise alternative to single-method anonymous inner classes.
  • Interfaces — inner classes frequently implement interfaces; understanding interfaces makes inner class usage click.
  • Garbage Collection Deep-Dive — explains why holding a hidden outer reference in a non-static inner class can cause memory leaks.
  • Access Modifiers — inner classes can have any access modifier; knowing what each modifier means helps you encapsulate inner classes properly.
Last updated June 13, 2026
Was this helpful?