Nested and Inner Classes in Java: A Complete Beginner’s Guide
-
Last Updated: July 29, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn nested and inner classes in Java the easy way. Static nested, inner, local, and anonymous classes explained with simple examples and interview tips.
You write a class. Then you need a small helper class that only makes sense inside the first one. Where do you put it?
You could make a separate file for it. But that helper has no life on its own. It exists only to serve the class next to it. Putting it far away feels wrong.
Picture a real moment. You are building a shopping cart class. Inside it, you want a tiny structure to hold one line item, with a product and a quantity. That line item never gets used anywhere else. It has no reason to be a big public class in its own file.
This is where nested and inner classes in Java come in. Java lets you place one class right inside another. The helper lives where it belongs, close to the code that uses it.
In this guide, we start simple. We look at why Java allows classes inside classes. Then we walk through each kind, one at a time, with tiny examples.
Here is what we will cover:
By the end, you will know which type to pick and why. That is the real goal, not just naming the four kinds.
You only need to know basic Java classes and objects. If you can write a class and make an object from it, you are ready to go.
Do not treat this as a topic you memorize for a test. Nested classes show up all over the real Java world. The JDK itself uses them a lot, from Map.Entry inside Map to the iterators hidden in every collection. Once you learn the pattern, you start spotting it everywhere.
A small warning before we dive in. The names sound close, and beginners often mix them up. Take each type slowly, and the differences will settle in. There is no rush.

A nested class is a class declared inside another class. The class on the outside is the outer class. The one tucked inside is the nested class.
Think of a car and its engine parts. A spark plug means little on its own. It only makes sense as a part of an engine. Some helper classes work the same way.
Java has allowed this since version 1.1. The feature is old and stable, so you can trust it fully. Every Java project you touch will likely use nested classes somewhere, even if you never wrote one yourself.
Here is the simplest form you can imagine:
public class Outer {
// this is a nested class
class Inner {
void hello() {
System.out.println("Hi from Inner");
}
}
}That is it. One class, and another class living inside its curly braces. From here, the details branch out into a few flavors, and those flavors are what the rest of this guide unpacks.
You nest a class for one main reason. It belongs to the outer class and nowhere else. Keeping them together makes your code easier to read.
Here are the real wins you get:
So a nested class is a tool for tidiness. It signals that two pieces of code are a team.
Take a real case. Say you build a LinkedList by hand. Each item needs a small Node holder, with a value and a link to the next node. That Node is useless anywhere else. Nesting it inside LinkedList keeps it hidden and close, exactly where it belongs.
Without nesting, you would have a loose Node class floating in your package. Other code could grab it by mistake. Nesting locks that door and keeps your design clean.
There is also a naming benefit. A top-level class must have a unique name in its package. A nested class only needs a name unique inside its outer class. So you can have Order.Item and Invoice.Item, and neither clashes with the other.
Java splits nested classes into two groups. The word you look for is static.
That single keyword changes a lot. A static nested class stands almost on its own. An inner class always rides along with an instance of the outer class.
The inner class family has three members. There is the regular inner class, the local class, and the anonymous class. We meet each one below.
A quick way to remember it: static means alone, no static means attached. If you can spot that one keyword, half the topic is already clear in your head.
It helps to see the whole set before we dig in. Here they are side by side:
| Type | Static? | Where declared | Needs outer object? |
|---|---|---|---|
| Static nested class | Yes | Inside a class | No |
| Inner class | No | Inside a class | Yes |
| Local class | No | Inside a method | Yes |
| Anonymous class | No | Inline, no name | Yes |
Do not worry if this looks like a lot. We take them one by one, and each gets its own short example.
Let us start with the easy one. A static nested class is a class inside a class, marked with static.
The static keyword does the heavy lifting here. It cuts the link to any outer object. The nested class stands on its own two feet.
A static nested class works without an instance of the outer class. You create it directly, using the outer class name as a prefix.
public class Computer {
// static nested class
static class USB {
String model = "USB 3.0";
}
}
// Creating it — no Computer object needed
Computer.USB usb = new Computer.USB();
System.out.println(usb.model); // Output: USB 3.0See the syntax? You write Computer.USB and never make a Computer first. The nested class is reachable through the outer class name alone.
In many ways, a static nested class acts like a normal top-level class. The only real difference is where it sits and how you name it. You keep it inside the outer class for the sake of grouping, not because it needs the outer object.
You can even mark a static nested class private. Then only the outer class can use it. This is a neat way to hide a helper that the outside world should never see.
A static nested class has limits on what it reaches. Since there is no outer object, it cannot use the outer instance fields.
This makes sense once you slow down. Instance fields belong to an object. With no object around, there is nothing to point at.
Here is a small example that shows the line clearly:
public class Config {
static String appName = "MyApp"; // static
String userId = "guest"; // instance
static class Logger {
void log() {
System.out.println(appName); // OK: static field
// System.out.println(userId); // ERROR: no instance
}
}
}The Logger reads appName without trouble. But the userId line would fail to compile. There is no Config object for the Logger to borrow that field from.
A common pattern is a Builder. Many classes ship a static nested Builder to help you make objects step by step.
public class Pizza {
private final String size;
private Pizza(Builder b) {
this.size = b.size;
}
// static nested builder
static class Builder {
private String size;
Builder size(String s) {
this.size = s;
return this;
}
Pizza build() {
return new Pizza(this);
}
}
}
Pizza p = new Pizza.Builder().size("Large").build();The Builder lives inside Pizza because it serves only Pizza. It is static, so you use it without a Pizza object. Clean and self-contained.
Why is a static nested class the right fit here? A Builder does not need any state from a Pizza. In fact, it runs before a Pizza even exists. Its whole job is to gather values and then hand them over to build one. So it has no reason to hold an outer object.
You will see this exact pattern across many real libraries. When a helper works alongside a class but does not depend on a live instance of it, the static nested class is almost always the answer.
| 💡 Interview Insight Interviewers love to ask why Map.Entry is a static nested interface. The short answer: an entry is just a key-value pair. It does not need the whole Map object to exist, so making it static keeps things light and avoids a needless link. |
A static nested class may hold its own static fields and methods. A plain inner class cannot, in most cases. This is another reason to reach for static when you can.
public class Server {
static class Config {
static int PORT = 8080; // allowed
static void printPort() {
System.out.println(PORT);
}
}
}
Server.Config.printPort(); // Output: 8080Here the Config class has a static PORT and a static method. You call them straight through the class name, with no object at all. That is a clean way to group related constants.
Try the same static field inside a non-static inner class, and the old Java rules push back. So if your helper needs its own statics, the static nested form is the natural home for it.
Now we drop the static keyword. A plain inner class is tied to an object of the outer class. This bond is the whole point.
An inner class cannot exist without an outer instance. You make the outer object first, then the inner one hangs off it.
Every inner class object holds a hidden link back to its outer object. Through that link, it can read everything the outer object has.
public class Engine {
private String fuel = "Petrol";
// inner class (no static)
class Piston {
void show() {
// can reach outer private field directly
System.out.println("Runs on " + fuel);
}
}
}Look at the Piston. It reads fuel with no fuss, even though fuel is private. The hidden link makes that possible.
You can picture that link as an invisible field named Engine.this. It points from the inner object back to the outer one. You rarely write it out, but it is always there under the hood.
When two fields share a name, you can use it to be clear:
public class Engine {
private String type = "V8";
class Piston {
private String type = "Steel";
void show() {
System.out.println(type); // Steel (inner)
System.out.println(Engine.this.type); // V8 (outer)
}
}
}The plain type gives the inner field. The Engine.this.type form reaches out to the outer field. This spelling saves you when names clash.
The syntax trips up a lot of beginners. You need an outer object first. Then you use a special form to make the inner one.
Engine engine = new Engine(); Engine.Piston piston = engine.new Piston(); piston.show(); // Output: Runs on Petrol
Notice the odd bit: engine.new Piston(). You call new on the outer object, not on the class. That spelling looks strange the first time.
This is the key difference from a static nested class. There you wrote new Computer.USB(). Here you must go through a live object.
| 💡 Interview Insight A common trick question: can you make an inner class object without an outer object? No. The inner class needs that hidden outer link, so a live outer instance must exist first. If you find yourself fighting this rule, the class probably wants to be static instead. |
A single outer object can own many inner objects. Each inner object links back to that same outer instance. They all share its state.
But two different outer objects give you two separate worlds. An inner object from one outer cannot read the fields of a different outer. The link is fixed at creation and never moves.
This is why the engine.new syntax matters. It tells Java which outer object the inner one should attach to. The choice is locked in from that moment on.
You can picture it like a badge. Each inner object wears a badge naming its home outer object. It only opens the doors of that one home, never anyone else’s.
Inner classes fit when the helper truly needs the outer state. If the helper must read or change outer fields, an inner class is a clean fit.
A classic example is an iterator inside a collection. The iterator needs to walk the outer object data. An inner class gives it direct access.
Here is a tiny box that holds items and offers a counter for them:
public class Box {
private String[] items = {"a", "b", "c"};
class Counter {
int howMany() {
return items.length; // reads outer array
}
}
Counter counter() {
return new Counter();
}
}
Box box = new Box();
System.out.println(box.counter().howMany()); // Output: 3The Counter reaches straight into the items array. It never receives that array as a parameter. The inner-class link hands it over for free.
But be careful with one thing. Since the inner object holds the outer object alive, it can block garbage collection. We come back to this trap later.
The two look almost the same in code. One keyword sets them apart, and that keyword changes how you use them. Let us put them next to each other.
| Point | Static nested | Inner class |
|---|---|---|
| Keyword | static | no static |
| Create with | new Outer.Nested() | outer.new Inner() |
| Outer object | Not needed | Required |
| Reads outer instance fields | No | Yes |
| Own static members | Yes | No (mostly) |
| Memory link to outer | None | Held alive |
Read that table slowly. Every row traces back to one thing: the hidden link. A static nested class drops the link, so it loses access to outer state but gains freedom. An inner class keeps the link, so it gains access but carries the outer object along.
Sometimes you need a class in just one method. Nowhere else. For that, Java offers the local class.
A local class is declared inside a method body. It lives and dies within that method. No other method can see it.
You write the class right in the middle of your code, like a variable. It only exists while that method runs.
public class Report {
void generate() {
// local class inside a method
class Header {
void print() {
System.out.println("=== Report Header ===");
}
}
Header h = new Header();
h.print();
}
}The Header class is invisible outside generate(). It is a private tool for that one method. That is the whole idea.
You might ask when this ever beats a normal helper. The answer is scope. A local class keeps a helper truly local. No other method can call it by accident, and it does not clutter the class body.
In practice, local classes are rare. Most of the time a private method or a static nested class does the job with less fuss. Still, it is good to know the tool exists, because you will meet it in older code.
A local class can use the local variables of its method. But there is a catch. Those variables must be final or effectively final.
Effectively final just means you never change the variable after you set it. Java treats it as final for you.
void greet(String name) {
// 'name' is effectively final (never reassigned)
class Greeter {
void hello() {
System.out.println("Hi, " + name);
}
}
new Greeter().hello();
}Here name is never changed, so the local class may read it. Try to reassign name later, and the compiler will complain.
This capture rule also applies to anonymous classes. Both types can freeze a snapshot of the method local variables. That is why you often pass values in and never touch them again.
You may wonder why the variable must stay final. The reason is quiet but simple.
The local object may outlive the method. So Java copies the variable value into the object. If the original could change, the two copies would drift apart. Java blocks that by forcing the value to stay put.
Think of it as a photocopy. The local class takes a photocopy of the value at capture time. Later edits to the original page would not show on the copy. To avoid that confusion, Java simply forbids the edits.
In real code, this rarely bites you. You usually read a value and leave it alone. But when the compiler does complain, now you know the reason behind it.
The last type has no name at all. An anonymous class is a class you write and use in one shot, inline.
You use it when you need a one-off object. Naming a whole class would be overkill. So you skip the name and write it on the spot.
You create an anonymous class while making an object. It usually implements an interface or extends a class right there.
interface Greeting {
void sayHello();
}
// anonymous class implementing Greeting
Greeting g = new Greeting() {
public void sayHello() {
System.out.println("Hello there!");
}
};
g.sayHello(); // Output: Hello there!There is no class name here. You write new Greeting() and then supply the body in braces. Java makes a hidden class for you behind the scenes.
If you peek at the compiled files, you will spot names like Outer$1.class. The number is Java giving the nameless class a label of its own. You never type that name, but the compiler needs one.
An anonymous class can also extend a real class, not just an interface:
Thread t = new Thread() {
public void run() {
System.out.println("Running in a thread");
}
};
t.start();Here the anonymous class extends Thread and overrides run(). You get a custom thread without writing a whole named subclass. That saves a lot of typing for a one-time need.
Anonymous classes show up a lot with listeners and callbacks. Old Java UI code used them heavily. You pass in behavior without a named class.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Button clicked!");
}
});The listener runs only for this one button. It needs no name and no reuse. An anonymous class fits the job well.
You also see them in sorting, threading, and event handling. Anywhere you must hand over a small block of behavior, an anonymous class can carry it. It keeps the logic right next to where you use it, so the reader does not hunt around.
You may have seen lambdas doing similar work. A lambda is shorter, and it often replaces an anonymous class.
But there is a line between them. A lambda works only for a functional interface, which has one method. An anonymous class can do more, like extend a class or add fields.
We do not dig into lambdas here. Just know that an anonymous class is the older, more flexible tool. The lambda is the sleek modern shortcut for simple cases.
| 💡 Interview Insight Watch the this keyword. Inside an anonymous class, this points to the anonymous object itself. Inside a lambda, this points to the outer object. That gap trips people up in interviews and in real bugs alike. |
Anonymous classes are handy, but they come with real limits. Knowing them helps you decide when to reach for a named class instead.
So keep anonymous classes small and short-lived. The moment one grows fields, logic, and many methods, it is begging to become a proper named class. Give it a name and a home.
One big perk runs through all these types. A nested class can reach the private members of its outer class. Let us look closer, because this is a point that surprises many people.
Normally, private means hidden from the outside. But a nested class is not really outside. It sits within the outer class.
public class Bank {
private double balance = 500.0;
class Account {
void show() {
// reads outer private field freely
System.out.println("Balance: " + balance);
}
}
}The Account class reads balance with ease. The private keyword does not block it. To Java, they are family.
The access goes both ways. The outer class can also reach the private members of its nested class.
public class Wallet {
private Card card = new Card();
private class Card {
private String number = "1234";
}
void show() {
// outer reads the nested private field
System.out.println(card.number);
}
}The Wallet reads card.number even though number is private. To Java, the outer and nested classes are one unit. Private walls do not stand between them.
So the two classes share a trust bond. This is why nested classes are great for helpers that need close cooperation.
There is a design lesson hiding here. Because a nested class can see private outer state, it can break your encapsulation if you are careless. Keep the nested class focused. Let it touch only what it truly needs, and no more.
Let us tie it all together with one small, realistic build. Say you write a tiny music playlist. It holds songs and hands out a player and a shuffle helper.
We will use two nested types here. One needs the playlist data, so it will be an inner class. The other does not, so it will be static. Watch how the choice falls out of the need.
First, the Playlist itself. It keeps a simple list of song names. Nothing fancy, just a place to hold state.
public class Playlist {
private String[] songs;
private int current = 0;
public Playlist(String[] songs) {
this.songs = songs;
}
}The songs array and the current index are private. Only the Playlist should touch them directly. Now we add helpers that lean on this state.
A Player must read the song list and track the current song. It cannot work without the Playlist data. So an inner class fits it well.
class Player {
void playNext() {
if (current < songs.length) {
System.out.println("Playing: " + songs[current]);
current++;
} else {
System.out.println("End of playlist");
}
}
}See how Player reads songs and current with no fuss. It never takes them as parameters. The inner-class link hands the outer state straight to it.
You create the Player through a live Playlist object, like this:
Playlist list = new Playlist(new String[]{"Song A", "Song B"});
Playlist.Player player = list.new Player();
player.playNext(); // Output: Playing: Song A
player.playNext(); // Output: Playing: Song BThe player moves through the songs and updates the shared index. Two calls, two songs. The state lives in the Playlist, and the inner Player rides along with it.
Now add a helper that shuffles an array. This helper does not care about any one Playlist. It just takes an array and mixes it. So it should be static.
static class Shuffler {
static void shuffle(String[] arr) {
for (int i = arr.length - 1; i > 0; i--) {
int j = (int) (Math.random() * (i + 1));
String tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}The Shuffler needs no Playlist object. You call it straight through the class name. It is a pure tool, grouped inside Playlist only because it belongs to that world.
String[] tracks = {"A", "B", "C"};
Playlist.Shuffler.shuffle(tracks); // no Playlist object neededLook back at the two helpers. The Player reads outer state, so it stayed non-static. The Shuffler did not, so it went static. You did not force the choice. The need made it for you.
This is the mindset to carry forward. Do not start by picking a type. Start by asking what the helper needs. The right nested class then shows up on its own.
Notice one more thing. Both helpers lived inside Playlist, close to the data they served. A reader opening this file sees the whole design in one place. That closeness is the quiet gift of nested classes.
A few traps catch beginners over and over. Let us name them so you can steer clear.
People often try new Piston() straight away. That fails for an inner class. You must write outer.new Piston() through a live outer object.
The error message can look confusing at first. Java complains that it cannot find an enclosing instance. Once you know the rule, the message makes sense. The inner class wants an outer object, and you gave it none.
The static keyword is easy to drop or add by mistake. Remember the rule. With static, no outer object is needed. Without it, an outer object is a must.
A stray static can also change meaning in a quiet way. Your code may still compile, but the class stops sharing the outer state you expected. Read the keyword twice when a nested class acts oddly.
In a local or anonymous class, a used local variable must stay effectively final. Change it after capture, and the code will not compile. Keep captured variables read-only.
A common slip is a loop counter. You cannot use a changing loop variable inside an anonymous class from an old-style for loop. Copy it into a fresh final variable first, then capture that copy.
An inner class object keeps its outer object alive. If the inner one lives long, the outer one cannot be collected. In big apps this leaks memory.
Picture a large screen object that hands out a small inner listener. If some long-lived cache holds that listener, it also pins the whole screen in memory. The screen sticks around long after you close it.
The fix is simple. Prefer a static nested class when you do not need the outer state. Without the hidden link, the outer object is free to go once you are done with it.
You can nest a class inside a nested class inside another class. Java allows it. But your reader will suffer. Deep nesting turns simple code into a maze.
Keep it shallow. One level of nesting is common and clear. Two levels is the rare exception. If you go deeper, that is a sign to pull a class out into its own file.
The same caution applies to size. A nested class should stay small and focused. When it grows into a big class with many methods, it stops feeling like a helper. At that point, give it its own file and let it breathe.
So which type do you pick? Here is a plain guide you can lean on.
When in doubt, lean toward the static nested class. It avoids the hidden link and the memory trap that comes with it.
Notice a pattern in that list. You only give up static when you truly need the outer object. So the question to ask is plain: does this helper read outer instance state? If not, keep it static and sleep easy.
Here is the full picture once more, now with a hint on when each fits:
| Type | Best for | Watch out for |
|---|---|---|
| Static nested | Standalone helpers, builders | Cannot read outer instance fields |
| Inner class | Helpers needing outer state | Can block garbage collection |
| Local class | One-method helpers | Captured vars must be final |
| Anonymous class | Quick one-off objects | Hard to reuse or test |
Keep this near you as a cheat sheet. Over time, the right pick will feel natural.
One last tip on choosing. When two options both seem to work, pick the simpler one. A static nested class is simpler than an inner class, because it drops the hidden link. So it wins any tie by default.
A: A static nested class is marked static and does not need an outer object. You create it with the outer class name, like new Outer.Nested(). An inner class is not static and holds a hidden link to an outer object, so you must make the outer object first and then call outer.new Inner().
A: No. A non-static inner class needs the hidden link to an outer instance, so a live outer object must exist first. If you do not need any outer state, make the class static instead, and then no outer object is required.
A: The class object may outlive the method, so Java copies the variable value into the object. If the original could change later, the two copies would drift apart. Java blocks that by requiring the value to stay fixed after it is set.
A: An anonymous class is a class with no name that you write and use in one shot. It usually implements an interface or extends a class right at the point of creation. It fits one-off needs like listeners and callbacks, where a full named class would be overkill.
A: An inner class object keeps its outer object alive through the hidden link. If the inner object lives a long time, the garbage collector cannot free the outer object either. To avoid this, prefer a static nested class whenever the helper does not need the outer state.
A: A lambda works only for a functional interface with one method and is shorter to write. An anonymous class is more flexible, since it can extend a class or add fields. Also, this inside an anonymous class points to the anonymous object, while this inside a lambda points to the outer object.
A: An entry is just a key-value pair and does not need the whole Map object to exist. Making it static keeps it lightweight and avoids a needless link back to the Map, which is exactly what a standalone helper should do.
A: Yes. A nested class sits within the outer class, so Java treats them as one unit. The nested class can read private outer members, and the outer class can read private members of the nested class too.
A: Use a static nested class when the helper does not read or change outer instance state, such as a Builder. It is the lighter choice and avoids the memory trap. Reach for an inner class only when the helper truly needs the outer object.
A: A local class is declared inside a method body and is visible only within that method. It works like a private helper for one method. In practice it is rare, since a private method or a static nested class often does the same job with less fuss.
Let us wrap up what we covered. Nested and inner classes in Java let you place one class inside another, close to where it belongs.
A static nested class stands alone and needs no outer object. The plain inner class rides along with an outer instance and can read its state. Local and anonymous classes handle the small, one-time jobs, one inside a method and one with no name at all.
Reach for the static nested class by default. Move to an inner class only when you truly need the outer object. Use local and anonymous classes for small, one-time jobs.
Do not stress about memorizing every rule right now. The one idea that carries you far is the static split. Static means alone, no static means attached to an outer object. Hold that thought, and the rest falls out of it.
Remember the lesson from the playlist walkthrough too. You did not pick a type first. You asked what each helper needed, and the right type showed up on its own. Carry that habit into your own code.
Play with each type in a small project. Write a builder, then an iterator, then a quick listener. Once you feel the difference between static and non-static by hand, the whole topic clicks into place.