class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
Именно такие классы и называют вложенными.
Они делятся на 2 вида:
- Non-static nested classes — нестатические вложенные классы. По-другому их еще называют inner classes — внутренние классы.
- Static nested classes — статические вложенные классы.
- локальным классом (local class)
- анонимным классом (anonymous class)

public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Поехали!");
}
public class HandleBar {
public void right() {
System.out.println("Руль вправо!");
}
public void left() {
System.out.println("Руль влево!");
}
}
public class Seat {
public void up() {
System.out.println("Сиденье поднято выше!");
}
public void down() {
System.out.println("Сиденье опущено ниже!");
}
}
}
Здесь у нас есть класс Bicycle
— велосипед. У него есть 2 поля и 1 метод — start()
.

Bicycle
— это классы HandleBar
(руль) и Seat
(сиденье).
Это полноценные классы: как видишь, у каждого из них есть собственные методы.
На этом моменте у тебя мог возникнуть вопрос: а зачем мы вообще засунули одни классы внутрь другого? Зачем делать их внутренними?
Ну ладно, допустим, нам нужны в программе отдельные классы для руля и сидения. Но ведь необязательно делать их вложенными! Можно же сделать обычные классы. Например, вот так:
public class HandleBar {
public void right() {
System.out.println("Руль вправо!");
}
public void left() {
System.out.println("Руль влево");
}
}
public class Seat {
public void up() {
System.out.println("Сиденье поднято выше!");
}
public void down() {
System.out.println("Сиденье опущено ниже!");
}
}
Очень хороший вопрос!
Конечно, технических ограничений у нас нет — можно сделать и так.
Здесь дело скорее в правильном проектировании классов с точки зрения конкретной программы и в смысле этой программы.
Внутренние классы — это классы для выделения в программе некой сущности, которая неразрывно связана с другой сущностью.
Руль, сиденье, педали — это составные части велосипеда. Отдельно от велосипеда они не имеют смысла.
Если бы мы сделали все эти классы отдельными публичными классами, в нашей программе мог бы появиться, к примеру такой код:
public class Main {
public static void main(String[] args) {
HandleBar handleBar = new HandleBar();
handleBar.right();
}
}
Эммм… Смысл этого кода даже объяснить сложно.
У нас есть какой-то непонятный велосипедный руль (зачем он нужен? Без понятия, если честно). И этот руль поворачивает вправо...сам по себе, без велосипеда...зачем-то.
Отделив сущность руля от сущности велосипеда, мы потеряли логику нашей программы.
С использованием внутреннего класса код смотрится совсем иначе:
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.HandleBar handleBar = peugeot.new HandleBar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handleBar.left();
handleBar.right();
}
}
Вывод в консоль:
Сиденье поднято выше!
Поехали!
Руль влево!
Руль вправо!
Происходящее внезапно обрело смысл! :)
Мы создали объект велосипеда. Создали два его «подобъекта» — руль и сиденье.
Подняли сиденье повыше для удобства — и поехали: катимся и рулим, куда надо! :)
Нужные нам методы вызываются у нужных объектов. Все просто и удобно.
В данном примере выделение руля и сидения усиливает инкапсуляцию (мы скрываем данные о частях велосипеда внутри соответствующего класса), и позволяет создать более подробную абстракцию.
Теперь давай рассмотрим другую ситуацию.
Допустим, мы хотим создать программу, моделирующую магазин велосипедов и их запчастей.

Объект внутреннего класса не может существовать без объекта «внешнего» класса.
Это логично: для того мы и сделали
Seat
иHandleBar
внутренними классами, чтобы в нашей программе не появлялись то тут, то там бесхозные рули и сиденья.Этот код не скомпилируется:
public static void main(String[] args) { HandleBar handleBar = new HandleBar(); }
Из этого вытекает следующая важная особенность:
У объекта внутреннего класса есть доступ к переменным «внешнего» класса.
Для примера давай добавим в наш класс
Bicycle
переменнуюint seatPostDiameter
— диаметр подседельного штыря.Тогда во внутреннем классе
Seat
мы можем создать методgetSeatParam()
, который сообщит нам параметр сиденья:public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Поехали!"); } public class Seat { public void up() { System.out.println("Сиденье поднято выше!"); } public void down() { System.out.println("Сиденье опущено ниже!"); } public void getSeatParam() { System.out.println("Параметр сиденья: диаметр подседельного штыря = " + Bicycle.this.seatPostDiameter); } } }
И теперь мы можем получить эту информацию в нашей программе:
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.getSeatParam(); } }
Вывод в консоль:
Параметр сиденья: диаметр подседельного штыря = 40
Обрати внимание: новая переменная объявлена с самым строгим модификатором —
private
. И все равно у внутреннего класса есть доступ!Объект внутреннего класса нельзя создать в статическом методе «внешнего» класса.
Это объясняется особенностями устройства внутренних классов. У внутреннего класса могут быть конструкторы с параметрами или только конструктор по умолчанию. Но независимо от этого, когда мы создаем объект внутреннего класса, в него незаметно передается ссылка на объект «внешнего» класса. Ведь наличие такого объекта — обязательное условие. Иначе мы не сможем создавать объекты внутреннего класса.
Но если метод внешнего класса статический, значит, объект внешнего класса может вообще не существовать! А значит, логика работы внутреннего класса будет нарушена. В такой ситуации компилятор выбросит ошибку:
public static Seat createSeat() { //Bicycle.this cannot be referenced from a static context return new Seat(); }
Внутренний класс не может содержать статические переменные и методы.
Логика здесь та же: статические методы и переменные могут существовать и вызваться даже при отсутствии объекта.
Но без объекта «внешнего» класса доступа к внутреннему классу у нас не будет.
Явное противоречие! Поэтому наличие статических переменных и методов во внутренних классах запрещено.
Компилятор выбросит ошибку при попытке их создать:
public class Bicycle { private int weight; public class Seat { //inner class cannot have static declarations public static void getSeatParam() { System.out.println("Параметр сиденья: диаметр подседельного штыря = " + Bicycle.this.seatPostDiameter); } } }
При создании объекта внутреннего класса важную роль играет его модификатор доступа.
Внутренний класс можно обозначить стандартными модификаторами доступа —
public
,private
,protected
иpackage private
.Почему это важно?
Это влияет на то, где в нашей программе мы сможем создавать экземпляры внутреннего класса.
Если наш класс
Seat
объявлен какpublic
, мы можем создавать его объекты в любом другом классе. Единственное требование — объект «внешнего» класса тоже обязательно должен существовать.Кстати, мы уже это делали вот здесь:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.HandleBar handleBar = peugeot.new HandleBar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handleBar.left(); handleBar.right(); } }
Мы легко получили доступ к внутреннему классу
HandleBar
из классаMain
.Если же мы объявим внутренний класс как
private
, доступ к созданию объектов у нас будет только внутри «внешнего» класса.Создать объект
Seat
снаружи мы уже не сможем:private class Seat { //методы } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); //Bicycle.Seat has a private access in 'Bicycle' Bicycle.Seat seat = bicycle.new Seat(); } }
Наверное, ты уже понял логику :)
Модификаторы доступа для внутренних классов работают так же, как и для обычных переменных.
Модификатор
protected
предоставляет доступ к переменной класса в его классах-наследниках и в классах, которые находятся в том же пакете.Так же
protected
работает и для внутренних классов. Объектыprotected
внутреннего класса можно создавать:- внутри «внешнего» класса;
- в его классах-наследниках;
- в тех классах, которые находятся в том же пакете.
Если у внутреннего класса нет модификатора доступа (
package private
), объекты внутреннего класса можно создавать- внутри «внешнего» класса;
- в классах, которые находятся в том же пакете.
С модификаторами ты уже давно знаком, так что тут проблем не будет.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
методахклассах можно объявлять статические переменные!