Java Inner Classes
Table of Contents + −
In the last lesson you learned about Java enum methods. Now we take “a class with extra powers” further. You can put one class inside another class. And one kind, the inner class, can reach right into the object that surrounds it.
🤔 Why put a class inside a class?
Picture a Notebook that keeps a list of pages. Each page belongs to one notebook and makes no sense on its own.
- A separate top-level
Pageclass looks like a big public thing, when it is really a private helper. - If a page needs the notebook’s settings, you would hand it a reference to the notebook every time.
- A nested class fixes this. You write the helper right inside the class that owns it.
- When the helper also needs the outer object’s data, you make it a non-static inner class. That is today’s topic.
🧱 An inner class is tied to an outer object
A non-static nested class is called an inner class. The key idea: every inner-class object is born attached to one outer-class object. It cannot exist on its own. Here an Engine lives inside a Car.
public class Car { String model = "Tesla";
class Engine { // inner class, no static keyword void describe() { System.out.println("Engine of " + model); // reads the outer field } }
public static void main(String[] args) { Car car = new Car(); // first make the outer object Car.Engine engine = car.new Engine(); // then make the inner one from it engine.describe(); }}The two lines in main show the whole rule.
- First build the outer object:
new Car(). - Then build the inner object from that car:
car.new Engine(). - The engine belongs to that one car, so it can read the car’s
model. - The type is written
Car.Engine, the outer name in front. Nostatickeyword is what makes it an inner class.
Output
Engine of Tesla🔑 An inner class can read the outer object’s fields, even private ones
This is the real reason inner classes exist. An inner-class object quietly holds a hidden link back to its outer object. Through that link, it can use the outer object’s fields and methods directly, even private ones. Here a private field and a private method are used straight from the inner class.
public class BankAccount { private double balance = 500; // private outer field
private void log(String msg) { // private outer method System.out.println("LOG: " + msg); }
class Statement { void print() { log("printing statement"); // calls the outer private method System.out.println("Balance is " + balance); // reads outer private field } }
public static void main(String[] args) { BankAccount account = new BankAccount(); BankAccount.Statement s = account.new Statement(); s.print(); }}print never received the balance as an argument.
- The
Statementobject keeps a hidden link to itsBankAccount. - Through that link it reaches
balanceandlog(...)by name. - Private does not block it, because the inner class is part of the same outer class.
Output
LOG: printing statementBalance is 500.0This close access is the whole point. The inner class works on behalf of one outer object, so Java lets it see everything that object has.
🛠️ A real use: a linked list with a Node helper
Inner classes shine for a helper that only makes sense inside one bigger structure. A linked list stores its items in small boxes called nodes, each pointing to the next. A Node is useless outside its list, so it belongs inside.
public class IntList { private Node head; // first node, or null when empty
class Node { // inner helper class int value; Node next;
Node(int value) { this.value = value; } }
void add(int value) { Node fresh = new Node(value); // inside the outer class we can just write new Node fresh.next = head; head = fresh; }
void printAll() { Node current = head; while (current != null) { System.out.print(current.value + " "); current = current.next; } System.out.println(); }
public static void main(String[] args) { IntList list = new IntList(); list.add(10); list.add(20); list.add(30); list.printAll(); }}One detail. Inside add we wrote new Node(value) with no car.new prefix. That works because add is an instance method of IntList, so an outer object already exists. You only need the outer.new Inner() form when you are outside the outer class.
Output
30 20 10🔁 Another use: an Iterator that walks the outer object
An iterator is a small object whose only job is to step through a collection one item at a time. It needs the collection’s data, so an inner class is ideal. This Bag holds a few names. Its inner BagIterator walks through them, reading the outer array directly.
public class Bag { private String[] items = { "pen", "book", "cup" };
class BagIterator { private int index = 0; // where we are in the walk
boolean hasNext() { return index < items.length; // reads the outer array }
String next() { return items[index++]; // returns one item, then moves on } }
public static void main(String[] args) { Bag bag = new Bag(); Bag.BagIterator it = bag.new BagIterator(); while (it.hasNext()) { System.out.println(it.next()); } }}The iterator keeps its own index, but the data stays in the Bag. Because BagIterator is an inner class, it reads items straight away. No need to copy the array or pass it in.
Output
penbookcup📍 A quick look at local classes
One more flavour: a local class, declared inside a method rather than at the top of the outer class. Use it when a helper is needed in just one method.
public class Demo { void run(String name) { class Greeter { // local class, lives only inside run void sayHi() { System.out.println("Hi " + name); // can use the method's variable } }
Greeter g = new Greeter(); // make it normally, right here g.sayHi(); }
public static void main(String[] args) { new Demo().run("Sam"); }}A local class is created with a plain new Greeter() because you are already inside the method. It can also read the method’s local variables, like name. Keep local classes for tiny one-off helpers. If one grows, move it to a named class.
Output
Hi Sam🔀 Inner class vs static nested class
We cover static nested classes fully next lesson, but here is the core difference.
- An inner class has no
statickeyword. It is tied to an outer object and needs one to exist. - A static nested class has the
statickeyword. It needs no outer object, and cannot reach the outer object’s instance fields.
This pair shows the contrast side by side.
public class Outer { private int value = 7;
class Inner { // inner: needs an Outer, can read value void show() { System.out.println(value); // ✅ reads the outer instance field } }
static class Nested { // static nested: no Outer needed void show() { System.out.println("no outer object here"); // System.out.println(value); // ❌ cannot reach an instance field } }
public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); // needs an outer object inner.show();
Outer.Nested nested = new Outer.Nested(); // no outer object needed nested.show(); }}The two creation lines say it all. outer.new Inner() starts from an existing outer object. new Outer.Nested() does not. Use an inner class when the helper truly needs the outer object’s data, a static nested class when it does not.
Output
7no outer object hereQuick test
Ask one question: does this helper need the outer object’s instance fields? If yes, make it an inner class. If no, add static and make it a static nested class. The default choice for most helpers is static.
⚠️ Common Mistakes
Trying to create an inner class with a plain new. From outside the outer class, an inner class needs an outer object first. A bare new will not compile.
// ❌ Avoid: no outer object, so this fails from outside OuterOuter.Inner inner = new Outer.Inner();
// ✅ Good: make the outer object, then make the inner one from itOuter outer = new Outer();Outer.Inner inner = outer.new Inner();Making it an inner class when it does not need the outer object. A non-static inner class secretly carries a link to the outer object. If the helper never uses it, that link is extra weight and keeps the outer object alive longer than needed. Add static instead.
// ❌ Avoid: inner class that never touches the outer objectpublic class Tree { class Node { // no outer field is ever used here int value; }}
// ✅ Good: it needs no outer object, so make it static nestedpublic class Tree { static class Node { int value; }}Confusing the inner type’s name. The inner class name carries the outer name in front. Writing just Engine from outside the class will not be found.
// ❌ Avoid: the bare name is not visible outside CarEngine engine = car.new Engine();
// ✅ Good: use the full nested name Car.EngineCar.Engine engine = car.new Engine();✅ Best Practices
- Reach for an inner class only when the helper needs the outer object’s data. If not, prefer a static nested class.
- Keep inner classes small and private when nothing outside should touch them.
- Create them with
outer.new Inner()from outside, plainnew Inner()from inside. - Use local classes for true one-off helpers. If one grows, promote it to a named class.
- Default to static for nested classes. Most helpers do not need the outer object. Drop
staticonly when you truly need the link.
🧩 What You’ve Learned
Great work. Let’s recap inner classes.
- ✅ An inner class is a non-static class defined inside another class.
- ✅ Each inner-class object is tied to one outer object and holds a hidden link to it.
- ✅ Through that link, an inner class can read the outer object’s fields and methods, even private ones.
- ✅ From outside, you create one with
outer.new Inner(); from inside an instance method, a plainnew Inner()works. - ✅ Inner classes fit helpers that need the outer’s state, like a
Nodein a list or an iterator. - ✅ A local class is declared inside a method for a one-off helper.
- ✅ A static nested class has the
statickeyword, needs no outer object, and cannot reach instance fields.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What makes a nested class an inner (non-static) class?
Why: An inner class has no static keyword. Each of its objects is attached to one outer object and can use that object's members.
- 2
From outside the outer class, how do you create an inner-class object?
Why: An inner class needs an outer object first, so you write outer.new Inner() using an existing outer instance.
- 3
Can an inner class read a private field of its outer object?
Why: An inner class is part of the outer class, so it can use the outer object's members directly, including private ones.
- 4
When should you use a static nested class instead of an inner class?
Why: If the helper never uses the outer object's data, make it static. A static nested class needs no outer object and avoids the hidden link.
🚀 What’s Next?
An inner class is great when it needs the outer object. But many nested helpers, like a Node or a Builder, never touch the outer object’s data at all. For those, you add the static keyword and skip the link entirely. Let’s see how that works.