00:00

Nested Classes in Java – Types, Rules and Examples

A nested class is a class declared inside another class. Java has four kinds: static nested classes, and three kinds of inner classes (non-static): member inner classes, local classes and anonymous classes. Nesting groups helper classes with the class they serve, strengthens encapsulation and can make code more readable.

This tutorial gives an overview of all four kinds in one runnable example, explains how nested classes access the outer class, and helps you choose the right one. Each kind has its own detailed tutorial.

1. Types of Nested Classes

Types of nested classes in Java: static nested and inner classes
KindDeclaredNeeds outer instance?Typical use
Static nested classAs a static memberNoHelpers, builders, node types (Map.Entry implementations)
Member inner classAs a non-static memberYesObjects tightly bound to one outer object (iterators)
Local classInside a method or blockYes, if in an instance methodHelpers needed in only one method
Anonymous classIn an expression, without a nameYes, if in an instance contextOne-off implementations of interfaces or abstract classes

2. All Four Kinds in One Example

AllNestedKinds.java
import java.util.function.Supplier;

public class AllNestedKinds {
    private String brand = "JavaByTechie";
    private static int created = 0;

    // 1. Static nested class: no outer instance needed
    static class Counter {
        int next() { return ++created; }                 // can use outer STATIC members
    }

    // 2. Member inner class: tied to an outer instance
    class Badge {
        String label() { return brand + " member"; }     // can use outer INSTANCE members
    }

    Supplier<String> greeter(String name) {
        // 3. Local class: declared inside a method
        class Greeting implements Supplier<String> {
            public String get() { return "Hello " + name + " from " + brand; }
        }
        return new Greeting();
    }

    Runnable announcer() {
        // 4. Anonymous class: declared and instantiated in one expression
        return new Runnable() {
            @Override public void run() { System.out.println("Anonymous: welcome to " + brand); }
        };
    }

    public static void main(String[] args) {
        AllNestedKinds outer = new AllNestedKinds();

        AllNestedKinds.Counter counter = new AllNestedKinds.Counter();   // no outer object
        System.out.println("Static nested: " + counter.next() + ", " + counter.next());

        AllNestedKinds.Badge badge = outer.new Badge();                  // needs an outer object
        System.out.println("Member inner:  " + badge.label());

        System.out.println("Local class:   " + outer.greeter("Asha").get());
        outer.announcer().run();
    }
}
Output
Static nested: 1, 2
Member inner:  JavaByTechie member
Local class:   Hello Asha from JavaByTechie
Anonymous: welcome to JavaByTechie
  • A static nested class is created like any class: new Outer.Nested().
  • A member inner class object needs an outer object: outer.new Inner().
  • Local and anonymous classes can use the method's parameters and local variables if they are effectively final (name above).

3. Access Between Outer and Nested Classes

A nested class and its outer class can access each other's private members. Since Java 11 they are formally nestmates, and the JVM grants this access directly:

PrivateAccess.java
public class PrivateAccess {
    private int secret = 42;

    class Inner {
        private int innerSecret = 7;
        int readOuter() { return secret; }                 // inner reads outer's private field
    }

    int readInner(Inner in) { return in.innerSecret; }     // outer reads inner's private field

    public static void main(String[] args) {
        PrivateAccess outer = new PrivateAccess();
        Inner in = outer.new Inner();
        System.out.println("Inner reads outer private: " + in.readOuter());
        System.out.println("Outer reads inner private: " + outer.readInner(in));
        System.out.println("Compiled class name: " + Inner.class.getName());
        System.out.println("Nestmates: " + PrivateAccess.class.isNestmateOf(Inner.class));
    }
}
Output
Inner reads outer private: 42
Outer reads inner private: 7
Compiled class name: PrivateAccess$Inner
Nestmates: true

Each nested class compiles to its own .class file named Outer$Inner.class; anonymous classes get numbers, such as Outer$1.class.

4. Why Use Nested Classes?

  • Logical grouping: a class used only by one other class lives next to it.
  • Encapsulation: a private nested class is invisible outside, yet can access the outer class's private members.
  • Readability: helper code is close to where it is used.
  • Callbacks and event handlers can be written inline (although lambdas are now preferred for functional interfaces).

5. Choosing the Right Kind

  1. Does it need access to an outer instance? If not, use a static nested class. This is the default choice.
  2. Does it need the outer instance and is used in several methods? Use a member inner class.
  3. Is it needed in only one method, and does it need a name (for example, to create several instances)? Use a local class.
  4. Is it a single, short implementation? Use an anonymous class, or a lambda if the target is a functional interface.

6. Pitfalls

  • Memory leaks: every inner class object holds a hidden reference to its outer object. A long-lived inner object (such as a listener or cached callback) keeps the outer object alive. Prefer static nested classes when the outer instance is not needed.
  • Serialization: serializing inner classes is discouraged because of the hidden outer reference and compiler-generated fields.
  • Shadowing: fields with the same name in inner and outer classes need Outer.this.field to disambiguate.

7. Summary

  • Nested classes are classes declared inside other classes.
  • Static nested classes need no outer instance; inner classes (member, local, anonymous) do.
  • Nested and outer classes can access each other's private members.
  • Prefer static nested classes unless you need the outer instance; use lambdas for functional interfaces.

Related Tutorials