Java: Inner, Nested & Anonymous Classes

20 Jul 2026 6 min read
1
A nested class is a class defined entirely within the body of another class (the enclosing or outer class).

Nested classes improve encapsulation, group related functionality together, and reduce unnecessary exposure of implementation details. Java supports four types of nested classes:

1. Static Nested Classes
2. Inner Classes (Non-static Nested Classes)
3. Local Inner Classes
4. Anonymous Classes

Static Nested Classes

A static nested class is declared using the static keyword inside another class.

Unlike other nested classes, it is associated with the outer class itself rather than an instance of the outer class.

Because it does not maintain a reference to an enclosing object, a static nested class can access only the static members of the outer class directly.

A static nested class is declared as follows.
public class Database {
    private static String url = "jdbc:mysql://localhost";

    public static class ConnectionFactory {

        public void printUrl() {
            System.out.println(url);
        }
    }
}
Unlike inner classes, a static nested class can be instantiated without creating an object of the enclosing class.
Database.ConnectionFactory factory =
        new Database.ConnectionFactory();
factory.printUrl();
A static nested class behaves much like a regular top-level class, except that it is logically grouped inside another class.

It can declare both instance and static members and can be instantiated without creating an instance of the enclosing class.

It can access the enclosing class's static members directly but cannot access its instance members without an explicit reference to an enclosing class object.

Inner Classes (Non-static Nested Classes)

An inner class is a non-static nested class declared directly inside another class.

Unlike a static nested class, every inner class object is associated with a specific instance of the enclosing class.

Because of this relationship, an inner class automatically has access to both the instance members and static members of its enclosing class, including private fields and methods.

Consider the following example.
public class Company {
    private String companyName = "OpenAI";

    public class Employee {
        public void printCompany() {
            System.out.println(companyName);
        }
    }
}
Creating an inner class requires an instance of the enclosing class.
Company company = new Company();
Company.Employee employee = company.new Employee();

employee.printCompany();
The expression company.new Employee() indicates that the inner class instance belongs to the specific company object.

Every inner class object implicitly stores a reference to its enclosing object. It can directly access the enclosing class's private members.

Unlike a static nested class, it cannot declare most static members.

Local Inner Classes

A local inner class is declared inside a method, constructor, or initialization block. Its scope is limited to the block in which it is declared, making it invisible outside that block.

Local inner classes are useful when a helper class is required only for a specific operation and has no meaning elsewhere in the application.

The following example declares a local inner class inside a method.
public class ReportGenerator {
    public void generateReport() {

        class Formatter {
            public void format() {
                System.out.println("Formatting report...");
            }
        }
        Formatter formatter = new Formatter();
        formatter.format();
    }
}
The Formatter class exists only while compiling the generateReport() method and cannot be accessed from any other method.

Local inner classes are designed for short-lived helper implementations. They can access members of the enclosing class.

They can access local variables only if they are final or effectively final. Like other inner classes, they cannot declare most static members.

Local inner classes are less common today because many of their use cases have been replaced by lambda expressions.

Anonymous Classes

An anonymous class is a local class without a name. It is declared and instantiated in a single expression and is typically used when a class is needed only once.

Anonymous classes are commonly used to provide a one-time implementation of an interface or abstract class without creating a separate named class.

Suppose an interface defines a callback.
public interface Greeting {
    void sayHello();
}
Instead of creating a separate implementation class, an anonymous class can be used directly.
Greeting greeting = new Greeting() {
    @Override
    public void sayHello() {
        System.out.println("Hello!");
    }
};
greeting.sayHello();
Anonymous classes can also extend abstract classes.
public abstract class Animal {
    abstract void speak();
}

Animal animal = new Animal() {
    @Override
    void speak() {
        System.out.println("Woof");
    }
};
animal.speak();
Since the class has no name, it cannot be reused elsewhere.

Before Java 8, anonymous classes were extensively used for event listeners and callbacks.

Today, many of these use cases are handled more concisely using lambda expressions, especially when implementing functional interfaces.

Variable Capture (Effectively Final)

Local inner classes and anonymous classes can access local variables declared in the enclosing method.

However, those variables must be final or effectively final. A variable is considered effectively final if it is assigned only once and is never modified after initialization.

This compiles successfully.
public void printMessage() {
    String message = "Hello";

    class Printer {
        void print() {
            System.out.println(message);
        }
    }
    new Printer().print();
}
The variable is never modified, so it is effectively final.

The following example does not compile.
public void printMessage() {
    String message = "Hello";
    message = "Hi"; // Modified

    class Printer {
        void print() {
            System.out.println(message);
        }
    }
}
Compilation Error:
Local variable message defined in an enclosing scope must be final or effectively final. 
This restriction prevents inconsistencies between the lifetime of local variables and the lifetime of objects that capture them, ensuring predictable behavior.

Shadowing and Outer Class References

When an inner class declares a field with the same name as a field in the enclosing class, the inner class field shadows the outer class field.

In such cases, the OuterClass.this syntax is used to explicitly refer to the enclosing object.

Consider the following example.
public class Company {
    private String name = "OpenAI";

    public class Employee {
        private String name = "John";

        public void printNames() {
            System.out.println(name);
            System.out.println(Company.this.name);
        }
    }
}
Output:
John OpenAI 
Here, name refers to the inner class field, while Company.this.name accesses the field belonging to the enclosing Company object.

Using OuterClass.this eliminates ambiguity and makes the code easier to understand when both classes define members with identical names.

Comparison of Nested Class Types

The following table summarizes the key differences among the four types of nested classes.
Feature Static Nested Inner Local Inner Anonymous
Declared Inside Class Class Method / Constructor Expression
Has a Name Yes Yes Yes No
Requires Outer Object No Yes Yes Yes (if inside an instance context)
Can Access Outer Instance Members No Yes Yes Yes
Can Access Method Local Variables No No Yes (Effectively Final) Yes (Effectively Final)
Typical Lifetime Reusable Reusable Method Scope Single Use

Final Notes

Nested classes provide a powerful mechanism for organizing related classes, improving encapsulation, and expressing ownership relationships within an application.

Static nested classes are ideal for helper classes that do not depend on an enclosing object, while inner classes naturally model objects that are tightly coupled to a specific instance of the enclosing class.

Local inner classes and anonymous classes are useful for short-lived implementations, particularly for callbacks, listeners, and temporary helper objects.

Modern Java continues to support anonymous classes, but many of their traditional use cases have been replaced by lambda expressions, which provide a more concise syntax for implementing functional interfaces.
Nagesh Chauhan

Nagesh Chauhan

Principal Software Engineer • Java • Python • Distributed Systems • AI/ML

Principal Software Engineer with 14+ years of experience designing and delivering large-scale distributed systems, cloud-native applications, and AI-powered platforms.

Passionate about solving complex engineering problems using strong data structures and algorithms, along with expertise in Java, Spring Boot, Python, System Design, Microservices, Cloud, Kafka, Elasticsearch, and Generative AI.

Share this Article

💬 Comments

Join the Discussion