Внутренние и вложенные классы java. Часть 1
Цель статьи: Рассказать о внутренних, вложенных, локальных, анонимных классах. Показать примеры их использования. Написать и протестировать классы в коде на java. Рассказать о свойствах этих классов. Материал предназначен для лучшего понимания безымянных классов, лямбда выражений, адаптеров и многопоточности. Авторское осмысление материала, позволяет посмотреть на уже известные вещи немного иначе. Такой взгляд помогает лучшему понимаю и большей ясности, того что уже известно.
Небольшое вступление. Предлагаю вашему вниманию цикл из трех статей.
В них я рассказываю о внутренних, вложенных, локальных, анонимных классах. Речь идет о терминологии и применении. Для этих статей я написал довольно много кода.
Это учебный код, а не руководство к действию. То есть сам код я написал для лучшего понимания. Также я постарался объяснить работу учебного кода. На написание данной публикации, ушло довольно много времени. Публикация состоит из трех частей. Прошу отнестись с пониманием.
Для лучшего изучения материала у вас должна быть некоторая подготовка.
То есть вам нужно знать: синтаксис языка java, область видимости переменных, классы, статические и нестатические члены класса, создание экземпляров класса, наследование, модификаторы доступа.
Начнем с того, что же такое внутренние и вложенные классы. Посмотрим терминологию, встречающуюся в документации и литературе:
В Java существуют 4 типа вложенных (nested) классов:
- Статические вложенные классы
- Внутренние классы
- Локальные классы
- Анонимные (безымянные) классы
Существуют четыре категории вложенных классов:
- статический класс-член (static member class),
- не статический класс-член (nonstatic member class),
- анонимный класс (anonymous class)
- и локальный класс (local class).
Попытаемся разобраться, что же это такое.
Начнем немного отдаленно, так как всё это имеет непосредственное отношение к нашим вопросам. Вспомним объектно-ориентированное программирование. Отношения композиции и наследования.
В своей книге «Java 2 Руководство разработчика» Майкл Морган очень хорошо и подробно описывает взаимосвязи классов и объектов. Мы рассмотрим некоторые из них. Взаимосвязь «это — есть — то» выражается наследованием, а взаимосвязь «имеет часть» описывается композицией.
В наших примерах мы в основном рассматриваем композицию. Так как вложенные классы — это и есть часть чего-то. То есть у нас есть класс оболочка и вложенный класс определенный внутри класса оболочки. Пример композиции: машина имеет двигатель, двери, 4 колеса, корпус. И мы можем описать машину с помощью внутренних (Inner) классов.
Пример такого использования вы можете найти в книге Брюса Эккеля «Философия Java»
/* Пример №1 */ //: c06:Car.java // композиция с использованием открытых объектов // двигатель class Engine < public void start()<>public void rev()<> public void stop()<> > class Wheel< public void inflare(int psi)<>// накачать > // окно class Window< public void rollup()<>// приоткрыть public void rolldown()<>// опустить > // дверь class Door< public Window window=new Window(); public void open()<>//открыть public void close()<>// закрыть > // машина public class Car < public Engine engine = new Engine(); public Wheel[] wheel = new Wheel[4]; public Door left = new Door(), right = new Door();//две двери public Car()< for(int i = 0; ipublic static void main(String[] args) < Car car= new Car(); car.left.window.rollup(); car.wheel[0].inflare(72); >>
Есть некоторое предупреждение автора по использованию кода в таком виде:
Так как композиция объекта является частью проведенного анализа задачи (а не
просто частью реализации класса), объявление членов класса открытыми, помогает программисту-клиенту понять, как использовать класс, и упрощает создателю написание кода. Однако нужно помнить, что описанный случай является особым, и в основном поля класса нужно объявлять как private.
Выше я не случайно упомянул наследование и композицию. Это напрямую относится к дальнейшему материалу.
Статические вложенные классы
Определение вложенных классов:
Класс называется вложенным (nested), если он определен внутри другого класса.
То есть класс просто определен внутри другого, даже не важно статически определен или не статически. Вложенный класс создается для того, чтобы обслуживать окружающий его класс. Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня.
Вложенные классы применяются в тех случаях, когда нужно написать небольшой вспомогательный код для другого класса. Вложенный класс создают также, чтобы скрыть его переменные и методы от внешнего мира. Таким образом, вложенный класс еще один элегантный способ ограничения области видимости. Внутренние классы также есть смысл использовать, если предполагается, что они будут использовать элементы родителя, чтобы не передавать лишнего в конструкторах.
Пример вложенного класса вы можете увидеть в документации Оракле:
/* Пример №2 */ // class OuterClass < . class NestedClass < . >>
У нас нет, пока что, никакого контекста использования данной конструкции. С таким же успехом вложенный класс мы можем назвать вместо: «Вложенный класс» (NestedClass) — «Внутренний класс» InnerClass. Далее будем разбираться, в чем же отличия, и в каких контекстах используются классы. Брюс Эккель пишет в книге «Философия Java» так:
«Класс называется вложенным (nested), если он определен внутри другого класса»
Документацию Oracle вы можете посмотреть по этой ссылке: >>>
Представим сильно упрощенную аналогию. Вложенные классы в Java напоминают матрешку — одну «куклу» вкладывают в другую. Например, если взять две куклы — большую и маленькую — и вложить маленькую в большую, то маленькая будет вложенным классом для большой. Такая ситуация отражает только «вложенность», но не отражает «взаимодействия».
Вложенные классы в Java при сильно упрощенной аналогии подобны тому, как один предмет может находиться внутри другого предмета. Например, книга может быть вложена в коробку.
Такая аналогия будет не совсем точна, потому как в данной ситуации не происходит похожих взаимодействий между предметами, подобных взаимодействий между классами.
Вложенные классы в Java похожи на то, как человек может быть внутри машины и управлять ей. Например, если человек находится внутри машины, то этот человек будет вложенным в машину. При этом он имеет возможность управлять машиной. Такая аналогия более приемлема.
Терминология:
Существует четыре категории вложенных классов:
- Статические вложенные классы и не статические вложенные классы. Вложенные классы, объявленные статически, называются вложенными статическими классами.
- Внутренние классы — когда объект внутреннего класса связан с объектом обрамляющего класса. Не статические вложенные классы называются внутренними классами, если они связанны с внешним классом.
- Локальные классы — объявленные внутри блока кода и не являющиеся членом обрамляющего класса. В этом случае можно рассматривать класс как локальную переменную типа класс.
- Анонимные классы – наследуемые, от какого либо класса, классы в которых при объявлении не задано имя класса.
Причины использования вложенных классов такие. Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование вложенных классов увеличивает инкапсуляцию. Рассмотрим два класса верхнего уровня, A и B, где B нужен доступ к членам, которые иначе были бы объявлены закрытыми.
«`java
/* Пример №3 */
//
class A …
class B …
>
>
«`
Скрывая класс «B» в пределах класса «А», члены класса «А» могут быть объявлены закрытыми, и «B» может получить доступ к ним. Кроме того, сам «B» может быть скрыт от внешнего мира.
Продемонстрируем это в коде:
«`java
/* Учебный пример №4 */
package innernested;
/**
*
* author Ar20L80
*/
public class A private static int iPrivVar;
class B void setPrivateOfA(int var)
A.iPrivVar = var;
>
>
>
«`
Использование вложенных классов приводит к более читабельному и поддерживаемому коду: Размещение класса ближе к тому месту, где он будет использован, делает код более читабельным.
Статические Вложенные Классы
Static Nested Classes
Причины использования статических вложенных классов такие.
Для случая, когда связь между объектом вложенного класса и объектом внешнего класса не нужна, можно сделать вложенный класс статическим(static).
Так как внутренний класс связан с экземпляром, он не может определить в себе любые статические члены.
Статические вложенные классы не имеют ограничений по объявлению своих данных и полей как static.
Из вложенного статического класса мы не имеем доступа к внешней не статической переменной внешнего класса.
Приведенный ниже код демонстрирует это:
«`java
/*
Учебный пример №5
Статические вложенные классы
Попытка доступа к не статической переменной
внешнего класса Outer2 через обращение из вложенного статического класса Nested2
*/
package nested;
/**
*
* author Ar20L80
* 20.03.2016
*/
public class Outer2 public int pubOutVar; // переменная не статическая и мы не имеем к ней доступа
// из внутреннего статического класса
private int prOutVar;
public Outer2()<>// конструктор внешнего класса
static class Nested2 public static int pub_innVar; // тут все в порядке
public Nested2() <> // конструктор вложенного класса
int getOuterPublicVariable()
return Outer2.this.pubOutVar; // ошибка
return Outer2.pubOutVar; // ошибка
>
int getOuterPrivateVariable()
return Outer2.this.prOutVar; // ошибка
return Outer2.prOutVar; // ошибка
>
Вывод: Мы не имеем доступа к не статическому полю внешнего класса, через статический контекст вложенного класса. Это подобно тому, как мы не имеем доступа из статического метода к нестатическим переменным класса. Точно также из статического вложенного класса мы не имеем доступа к нестатическим переменным внешнего класса.
Но мы имеем доступ к приватным статическим полям внешнего класса из вложенного статичного класса.
Приведенный ниже фрагмент кода демонстрирует это:
«`java
/*
Учебный пример №6
Статические вложенные классы
Демонстрация доступа к «приватной» статической переменной
внешнего класса из внутреннего статического класса
20.03.2016
*/
package nested;
/**
*
* author Ar20L80
*/
public class Outer3 private static int prStOuterVar;
public Outer3()<>
static class Nested3 // Nested
<
int getStaticOuterVar()
return Outer3.prStOuterVar; // ok
>
void setStaticOuterVariable(int var)
Outer3.prStOuterVar = var; // ok
>
>
public static void main(String[] args) Outer3.Nested3 nestedObj = new Outer3.Nested3(); // экземпляр класса внутренний
System.out.println(«nestedObj.getStaticOuterVar() = „+nestedObj.getStaticOuterVar());//статическая переменная внешнего класса из экземпляра внутреннего
// устанавливаем через экземпляр внутреннего класса
nestedObj.setStaticOuterVariable(77);
System.out.println(“Outer3.prStOuterVar = „+ Outer3.prStOuterVar);
>
>
/*
Вывод программы:
nestedObj.getStaticOuterVar() = 19
Outer3.prStOuterVar = 77
*/
«`
В этом примере кода мы создали экземпляр внутреннего класса с именем “nestedObj».
То есть мы получаем доступ к приватной статической переменной внешнего класса, через экземпляр внутреннего класса. В контексте экземпляра связанного с внешним классом, у нас получился внутренний класс.
Майкл Морган. «Java 2. Руководство разработчика» ISBN 5-8459-0046-8
Брюс Эккель. «Философия Java.» ISBN 5-272-00250-4
Герберт Шилдт «Java. Полное руководство. 8-е издание.» ISBN: 978-5-8459-1759-1
Все вопросы, комментарии, дополнения, критика приветствуются.
Для вашего комфорта, я создал репозиторий на github.
Теперь вы можете скачать актуальные версии кода, для ваших экспериментов, тестов и изучения:
https://github.com/vvm64/InnerAndNestedCl
PS. Нумерация материалов может не соответствовать нумерации материалов в статье.
Продолжение следует…
Часть 2 >>>
- Программирование
- Java
- Проектирование и рефакторинг
- ООП
Классы, виды вложенных классов с примерами


Всем привет. В данной теме я хочу подробно рассказать про классы Java и их виды, чтобы помочь новичкам разобраться с данной темой, и, возможно, не новичкам узнать что-то новое. По возможности, все будет показано на примерах из реальной жизни с прилегающими примерами кода. Давайте приступать. И хотелось бы отметить, что главное осознать первые два вида классов, а локальные и анонимные — это просто подвиды внутреннего класса.
Что такое класс?
Класс – логическое описание чего-либо, шаблон, с помощью которого можно создавать реальные экземпляры этого самого чего-либо. Другими словами, это просто описание того, какими должны быть созданные сущности: какими свойствами и методами должны обладать. Свойства – характеристики сущности, методы – действия, которые она может выполнять. Хорошим примером класса из реальной жизни, дающим понимание, что же такое класс, можно считать чертежи: чертежи используются для описания конструкций (катапульта, отвертка), но чертеж – это не конструкция. Инженеры используют чертежи, чтобы создавать конструкции, так и в программировании классы используются для того, чтобы создавать объекты, обладающие описанными свойствами и методами.
public class Student < private String name, group, specialty; public Student(String name, String group, String specialty) < this.name = name; this.group = group; this.specialty = specialty; >// getters/setters >
В данном примере мы создали Java класс, описывающий сущность «студент»: у каждого студента есть имя, группа и специальность. Теперь в других местах программы мы можем создавать реальные образцы данного класса. Другими словами: если класс Student — это портрет того, какими должны быть студент, то созданный экземпляр — это непосредственно реальный студент. Пример создания нового студента: new Student(«Ivan», «KI-17-2», «Computer Engineering»); Оператор new ищет класс Student , после чего вызывает специальный метод (конструктор) данного класса. Конструктор возвращает готовый объект класса Student — нашего родного, голодного студента без стипендии :))
Виды классов в Java
- Вложенные внутренние классы – нестатические классы внутри внешнего класса.
- Вложенные статические классы – статические классы внутри внешнего класса.
- Локальные классы Java – классы внутри методов.
- Анонимные Java классы – классы, которые создаются на ходу.
Нестатические классы внутри внешнего класса

Сначала, я хочу, чтобы вы осознали, что это такое, на реальном примере, потому что это облегчает понимание в разы. Так что сейчас мы будем разбивать реальную большую вещь на более мелкие составные части, а разбирать мы будем – самолёт! Однако, для примера будет достаточно показать немного, полностью разбивать мы не будем. Для визуализации данного процесса, будем использовать схему самолёта. Для начала, нам нужно создать класс Airplane , куда мы можем занести немного описание: название самолета, идентификационный код, рейс.
public class Airplane < private String name, id, flight; public Airplane(String name, String id, String flight) < this.name = name; this.id = id; this.flight = flight; >// getters/setters >
Теперь мы хотим добавить крылья. Создавать отдельный класс? Возможно в этом и есть логика, если у нас сложная программа для конструирования самолетов, и где нам нужно создавать огромное количество производных классов (классы, которые обладают такой же логикой, как и родительский класс, то есть класс, от которого они наследуются, но так же расширяют родительский класс, добавляя логику или более подробные характеристики), но что, если у нас просто игра, где у нас есть один самолет? Тогда нам будет рациональней укомплектовать всю структуру в одном месте (в одном классе). Тут идут в бой нестатические вложенные классы. По сути, это более подробное описание каких-то деталей нашего внешнего класса. В данном примере, нам нужно создать крылья для самолета – левое и правое. Давайте создавать!
public class Airplane < private String name, id, flight; private Wing leftWing = new Wing("Red", "X3"), rightWing = new Wing("Blue", "X3"); public Airplane(String name, String id, String flight) < this.name = name; this.id = id; this.flight = flight; >private class Wing < private String color, model; private Wing(String color, String model) < this.color = color; this.model = model; >// getters/setters > // getters/setters >
- Они существуют только у объектов, потому для их создания нужен объект. Другими словами: мы укомплектовали наше крыло так, чтобы оно было частью самолета, потому, чтобы создать крыло, нам нужен самолет, иначе оно нам не нужно.
- Внутри Java класса не может быть статических переменных. Если вам нужны какие-то константы или что-либо еще статическое, выносить их нужно во внешний класс. Это связано с тесной связью нестатического вложенного класса с внешним классом.
- У класса полный доступ ко всем приватным полям внешнего класса. Данная особенность работает в две стороны.
- Можно получить ссылку на экземпляр внешнего класса. Пример: Airplane.this – ссылка на самолет, this – ссылка на крыло.
Статические классы внутри внешнего класса
Данный вид классов не отличается ничем от обычного внешнего класса, кроме одного: для создания экземпляра такого класса, нужно через точку перечислить весь путь от внешнего класса до нужного. Например: Building.Plaftorm platform = new Building.Platform(); Статические классы используются для того, чтобы укомплектовать связанные классы рядышком, чтобы с логической структурой было работать проще. Например: мы можем создать внешний класс Building , где будет конкретный список классов, которые будут представлять из себя уже конкретную постройку.
public abstract class Building < private String name, address, type; Building(String name, String address) < this.name = name; this.address = address; >public static class Platform extends Building < public Platform(String name, String address) < super(name, address); setType("Platform"); >// some additional logic > public static class House extends Building < public House(String name, String address) < super(name, address); setType("House"); >// some additional logic > public static class Shop extends Building < public Shop(String name, String address) < super(name, address); setType("Shop"); >// some additional logic > // getters/setters >
- Количество классов уменьшилось.
- Все классы внутри их класса-родителя. Мы способны прослеживать всю иерархию без открытия каждого класса отдельно.
- Мы можем обратиться к классу Building, а IDE уже будет подсказывать весь список всех подклассов данного класса. Это будет упрощать поиск нужных классов и показывать всю картину более цело.
Локальные классы
- Локальные классы способны работать только с final переменными метода. Все дело в том, что экземпляры локальных классов способны сохраняться в «куче» после завершения работы метода, а переменная может быть стёрта. Если же переменная объявлена final, то компилятор может сохранить копию переменной для дальнейшего использования объектом. И еще: с 8+ версий Java можно использовать не final переменные в локальных классах, но только при условии, что они не будут изменяться.
- Локальные классы нельзя объявлять с модификаторами доступа.
- Локальные классы обладают доступом к переменным метода.
public class Person < private String name, street, house; public Person(String name, String street, String house) < this.name = name; this.street = street; this.house = house; >private interface AddressContainer < String getStreet(); String getHouse(); >public AddressContainer getAddressContainer() < class PersonAddressContainer implements AddressContainer < final String street = Person.this.street, house = Person.this.house; @Override public String getStreet() < return this.street; >@Override public String getHouse() < return this.house; >> return new PersonAddressContainer(); > public static void main(String[] args) < Person person = new Person("Nikita", "Sholohova", "17"); AddressContainer address = person.getAddressContainer(); System.out.println("Address: street - " + address.getStreet() + ", house - " + address.getHouse()); >// getters/setters >
Как можно заметить, внутри метода мы создали класс, реализующий хранилище местоположения человека, создали там константные переменные (чтобы после выхода из метода переменные хранились в объекте) и реализовали метод для получения адреса и дома. Теперь мы можем использовать данный объект в других местах программы, чтобы получать местоположение человека. Понимаю, что данный пример неидеальный и его было правильней сделать просто оставив геттеры в классе Person , однако создание данного класса и его возможное использование было показано, а далее решать вам.
Анонимные классы
Под капотом анонимные классы – просто обычные нестатические вложенные классы. Их особенность в удобстве их использования. Вы можете написать свой класс прямо при создании экземпляра другого класса.
public class Animal < public void meow() < System.out.println("Meow!"); >public static void main(String[] args) < Animal anonTiger = new Animal() < @Override public void meow() < System.out.println("Raaar!"); >>; Animal notAnonTiger = new Animal().new Tiger(); anonTiger.meow(); // будет выведено Raaar! notAnonTiger.meow(); // будет выведено Raaar! > private class Tiger extends Animal < @Override public void meow() < System.out.println("Raaar!"); >> >
- тело класса является очень коротким;
- нужен только один экземпляр класса;
- класс используется в месте его создания или сразу после него;
- имя класса не важно и не облегчает понимание кода.
JButton b2 = new JButton("Click"); b2.addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < System.out.println("Кнопка нажата!"); >>);
Однако после Java 8 начали использовать лямбда-выражения, но все равно много кода было написано до 8 версии и вы можете столкнуться (и столкнетесь в ходе обучения на JavaRush) с такими вот надписями.\ Аналог с лямбдами:
JButton b2 = new JButton("Click"); b2.addActionListener(e -> System.out.println("Кнопка нажата!"));
Конец статьи Спасибо всем за внимание и надеюсь, что вы узнали что-нибудь новое или разобрались в чем-то, чего раньше не понимали. Хочу так же уточнить, что данная статья относится к номинации «внимание к деталям». Это моя первая работа, так что буду рассчитывать, что кому-то она была полезна. В ближайшее время, когда придут новые идеи, буду пытаться написать еще что-то, как раз есть одна идея. Удачи всем и успехов в программировании 🙂
22. Java – Вложенные и внутренние классы
В Java, подобно методам, переменные класса тоже могут иметь в качестве своего члена другой класс. В Java допускается написание класса внутри другого. Класс, написанный внутри, называется в Java вложенным классом, а класс, который содержит внутренний класс, называется внешним классом.
Синтаксис
Ниже приведен синтаксис для записи вложенного класса. Здесь класс Outer_Demo – это внешний класс, а класс Inner_Demo – вложенный класс.
class Outer_Demo < class Nested_Demo < >>
Вложенные классы в Java делятся на два типа:

- Нестатические вложенные классы – нестатические члены класса.
- Статические вложенные классы – статические члены класса.
Внутренние классы (нестатические вложенные классы)
Внутренние классы – это механизм безопасности в Java. Мы уже знаем, что класс не может быть связан с модификатором доступа private, но если у нас есть класс как член другого класса, то внутренний класс может быть private. И это также используется для доступа к закрытым (private) членам класса.
В Java внутренние классы имеют три типа в зависимости от того, как и где Вы их определяете:
- внутренний класс;
- локальный метод внутреннего класса;
- анонимный внутренний класс.
Внутренние классы
Создать внутренний класс в Java довольно просто. Вам просто нужно написать класс внутри класса. В отличие от класса, внутренний класс может быть закрытым (private), и после того, как Вы объявляете внутренний класс закрытым, он не может быть доступен из объекта вне класса.
Ниже приведен пример создания внутреннего класса и получения доступа к нему. В данном примере мы делаем внутренний класс private и получаем доступ к классу с помощью метода.
Пример
class Outer_Demo < int num; // Внутренний класс private class Inner_Demo < public void print() < System.out.println("Это внутренний класс"); >> // Доступ к внутреннему классу из метода void display_Inner() < Inner_Demo inner = new Inner_Demo(); inner.print(); >> public class My_class < public static void main(String args[]) < // Создание внешнего класса Outer_Demo outer = new Outer_Demo(); // Доступ к методу display_Inner() outer.display_Inner(); >>
Здесь Вы можете заметить, что Outer_Demo – внешний класс, Inner_Demo – внутренний класс, display_Inner() – метод, внутри которого мы создаем внутренний класс, и этот метод вызывается из основного метода.
Если Вы скомпилируете и выполните вышеуказанную программу, Вы получите следующий результат:
Это внутренний класс
Доступ к частным (private) членам
Как упоминалось ранее, внутренние классы также используются в Java для доступа к закрытым членам класса. Предположим, у класса есть private члены. Для доступа к ним напишите в нем внутренний класс, верните частные члены из метода внутри внутреннего класса, скажем, методом getValue() и, наконец, из другого класса (из которого Вы хотите получить доступ к закрытым членам) вызовите метод getValue() внутреннего класса.
Чтобы создать экземпляр внутреннего класса, сначала Вам необходимо создать экземпляр внешнего класса. После этого, используя объект внешнего класса, Вы можете создать экземпляр внутреннего класса.
Outer_Demo outer = new Outer_Demo(); Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();
Следующий пример показывает, как получить доступ к закрытым членам класса с использованием внутреннего класса.
Пример
class Outer_Demo < // Частная переменная внешнего класса private int num = 2018; // Внутренний класс public class Inner_Demo < public int getNum() < System.out.println("Это метод getnum внутреннего класса"); return num; >> > public class My_class2 < public static void main(String args[]) < // Создание внешнего класса Outer_Demo outer = new Outer_Demo(); // Создание внутреннего класса Outer_Demo.Inner_Demo inner = outer.new Inner_Demo(); System.out.println(inner.getNum()); >>
Если Вы скомпилируете и выполняете вышеуказанную программу, то получите следующий результат:
Это метод getnum внутреннего класса 2018
Локальный метод внутреннего класса
В Java мы можем написать класс внутри метода, и это будет локальный тип. Как и локальные переменные, возможности внутреннего класса ограничены в рамках метода.
Локальный метод внутреннего класса может быть создан только внутри метода, где определяется внутренний класс. Следующая программа показывает, как использовать локальный внутренний метод.
Пример
public class Outerclass < // Метод экземпляра внешнего класса void my_Method() < int num = 888; // Локальный метод внутреннего класса class MethodInner_Demo < public void print() < System.out.println("Это метод внутреннего класса: " + num); >> // Конец внутреннего класса // Доступ к внутреннему классу MethodInner_Demo inner = new MethodInner_Demo(); inner.print(); > public static void main(String args[]) < Outerclass outer = new Outerclass(); outer.my_Method(); >>
Получим следующий результат:
Это метод внутреннего класса: 888
Анонимные внутренние классы в Java
Анонимный внутренний класс – это внутренний класс, объявленный без имени класса. В случае анонимных внутренних классов в Java мы объявляем и создаем их в одно и то же время. Как правило, они используются всякий раз, когда Вам необходимо переопределить метод класса или интерфейса. Синтаксис анонимного внутреннего класса в Java выглядит следующим образом:
Синтаксис
AnonymousInner an_inner = new AnonymousInner() < public void my_method() < . . >>;
Пример
Следующая программа показывает, как переопределить метод класса с использованием анонимного внутреннего класса.
abstract class AnonymousInner < public abstract void mymethod(); >public class Outer_class < public static void main(String args[]) < AnonymousInner inner = new AnonymousInner() < public void mymethod() < System.out.println("Это пример анонимного внутреннего класса"); >>; inner.mymethod(); > >
Получим следующий результат:
Это пример анонимного внутреннего класса
Точно так же Вы можете переопределить методы конкретного класса, а также интерфейс, используя в Java анонимный внутренний класс.
Анонимный внутренний класс как аргумент
Как правило, если метод принимает объект интерфейса, абстрактный класс или конкретный класс, то мы можем реализовать интерфейс, расширить абстрактный класс и передать объект методу. Если это класс, мы можем напрямую передать его методу.
Но во всех трех случаях Вы можете в Java передать анонимный внутренний класс методу. Синтаксис передачи анонимного внутреннего класса в качестве аргумента метода:
Синтаксис
obj.my_Method(new My_Class() < public void Do() < . . >>);
Пример
Следующая программа показывает, как передать анонимный внутренний класс в качестве аргумента метода.
// Интерфейс interface Message < String greet(); >public class My_class < // Метод, который принимает объект интерфейса Message public void displayMessage(Message m) < System.out.println(m.greet() + ", это пример анонимного внутреннего класса в качестве аргумента"); >public static void main(String args[]) < // Создание класса My_class obj = new My_class(); // Передача анонимного внутреннего класса в качестве аргумента obj.displayMessage(new Message() < public String greet() < return "Привет"; >>); > >
Если Вы скомпилируете и выполните вышеуказанную программу, то получите следующий результат:
Привет, это пример анонимного внутреннего класса в качестве аргумента
Статический вложенный класс в Java
Статический внутренний класс – это вложенный класс, который является статическим членом внешнего класса. Доступ к нему возможен без создания экземпляра внешнего класса с использованием других статических элементов. Как и статические члены, статический вложенный класс не имеет доступа к переменным экземпляра и методам внешнего класса. Синтаксис статического вложенного класса в Java выглядит следующим образом:
Синтаксис
class MyOuter < static class Nested_Demo < >>
Пример
Создание экземпляра статического вложенного класса немного отличается от экземпляра внутреннего класса. Следующая программа показывает, как использовать статические вложенные классы.
public class Outer < static class Nested_Demo < public void my_method() < System.out.println("Это мой вложенный класс"); >> public static void main(String args[]) < Outer.Nested_Demo nested = new Outer.Nested_Demo(); nested.my_method(); >>
Получим следующий результат:
Это мой вложенный класс
Оглавление
- 1. Java – Самоучитель для начинающих
- 2. Java – Обзор языка
- 3. Java – Установка и настройка
- 4. Java – Синтаксис
- 5. Java – Классы и объекты
- 6. Java – Конструкторы
- 7. Java – Типы данных и литералы
- 8. Java – Типы переменных
- 9. Java – Модификаторы
- 10. Java – Операторы
- 11. Java – Циклы и операторы цикла
- 11.1. Java – Цикл while
- 11.2. Java – Цикл for
- 11.3. Java – Улучшенный цикл for
- 11.4. Java – Цикл do..while
- 11.5. Java – Оператор break
- 11.6. Java – Оператор continue
- 12. Java – Операторы принятия решений
- 12.1. Java – Оператор if
- 12.2. Java – Оператор if..else
- 12.3. Java – Вложенный оператор if
- 12.4. Java – Оператор switch..case
- 12.5. Java – Условный оператор (? 🙂
- 13. Java – Числа
- 13.1. Java – Методы byteValue(), shortValue(), intValue(), longValue(), floatValue(), doubleValue()
- 13.2. Java – Метод compareTo()
- 13.3. Java – Метод equals()
- 13.4. Java – Метод valueOf()
- 13.5. Java – Метод toString()
- 13.6. Java – Метод parseInt()
- 13.7. Java – Метод Math.abs()
- 13.8. Java – Метод Math.ceil()
- 13.9. Java – Метод Math.floor()
- 13.10. Java – Метод Math.rint()
- 13.11. Java – Метод Math.round()
- 13.12. Java – Метод Math.min()
- 13.13. Java – Метод Math.max()
- 13.14. Java – Метод Math.exp()
- 13.15. Java – Метод Math.log()
- 13.16. Java – Метод Math.pow()
- 13.17. Java – Метод Math.sqrt()
- 13.18. Java – Метод Math.sin()
- 13.19. Java – Метод Math.cos()
- 13.20. Java – Метод Math.tan()
- 13.21. Java – Метод Math.asin()
- 13.22. Java – Метод Math.acos()
- 13.23. Java – Метод Math.atan()
- 13.24. Java – Метод Math.atan2()
- 13.25. Java – Метод Math.toDegrees()
- 13.26. Java – Метод Math.toRadians()
- 13.27. Java – Метод Math.random()
- 14. Java – Символы
- 14.1. Java – Метод Character.isLetter()
- 14.2. Java – Метод Character.isDigit()
- 14.3. Java – Метод Character.isWhitespace()
- 14.4. Java – Метод Character.isUpperCase()
- 14.5. Java – Метод Character.isLowerCase()
- 14.6. Java – Метод Character.toUpperCase()
- 14.7. Java – Метод Character.toLowerCase()
- 14.8. Java – Метод Character.toString()
- 15. Java – Строки
- 15.1. Java – Метод charAt()
- 15.2. Java – Метод compareTo()
- 15.3. Java – Метод compareToIgnoreCase()
- 15.4. Java – Метод concat()
- 15.5. Java – Метод contentEquals()
- 15.6. Java – Метод copyValueOf()
- 15.7. Java – Метод endsWith()
- 15.8. Java – Метод equals()
- 15.9. Java – Метод equalsIgnoreCase()
- 15.10. Java – Метод getBytes()
- 15.11. Java – Метод getChars()
- 15.12. Java – Метод hashCode()
- 15.13. Java – Метод indexOf()
- 15.14. Java – Метод intern()
- 15.15. Java – Метод lastIndexOf()
- 15.16. Java – Метод length()
- 15.17. Java – Метод matches()
- 15.18. Java – Метод regionMatches()
- 15.19. Java – Метод replace()
- 15.20. Java – Метод replaceAll()
- 15.21. Java – Метод replaceFirst()
- 15.22. Java – Метод split()
- 15.23. Java – Метод startsWith()
- 15.24. Java – Метод subSequence()
- 15.25. Java – Метод substring()
- 15.26. Java – Метод toCharArray()
- 15.27. Java – Метод toLowerCase()
- 15.28. Java – Метод toString()
- 15.29. Java – Метод toUpperCase()
- 15.30. Java – Метод trim()
- 15.31. Java – Метод valueOf()
- 15.32. Java – Классы StringBuilder и StringBuffer
- 15.32.1. Java – Метод append()
- 15.32.2. Java – Метод reverse()
- 15.32.3. Java – Метод delete()
- 15.32.4. Java – Метод insert()
- 15.32.5. Java – Метод replace()
- 16. Java – Массивы
- 17. Java – Дата и время
- 18. Java – Регулярные выражения
- 19. Java – Методы
- 20. Java – Потоки ввода/вывода, файлы и каталоги
- 20.1. Java – Класс ByteArrayInputStream
- 20.2. Java – Класс DataInputStream
- 20.3. Java – Класс ByteArrayOutputStream
- 20.4. Java – Класс DataOutputStream
- 20.5. Java – Класс File
- 20.6. Java – Класс FileReader
- 20.7. Java – Класс FileWriter
- 21. Java – Исключения
- 21.1. Java – Встроенные исключения
- 22. Java – Вложенные и внутренние классы
- 23. Java – Наследование
- 24. Java – Переопределение
- 25. Java – Полиморфизм
- 26. Java – Абстракция
- 27. Java – Инкапсуляция
- 28. Java – Интерфейсы
- 29. Java – Пакеты
- 30. Java – Структуры данных
- 30.1. Java – Интерфейс Enumeration
- 30.2. Java – Класс BitSet
- 30.3. Java – Класс Vector
- 30.4. Java – Класс Stack
- 30.5. Java – Класс Dictionary
- 30.6. Java – Класс Hashtable
- 30.7. Java – Класс Properties
- 31. Java – Коллекции
- 31.1. Java – Интерфейс Collection
- 31.2. Java – Интерфейс List
- 31.3. Java – Интерфейс Set
- 31.4. Java – Интерфейс SortedSet
- 31.5. Java – Интерфейс Map
- 31.6. Java – Интерфейс Map.Entry
- 31.7. Java – Интерфейс SortedMap
- 31.8. Java – Класс LinkedList
- 31.9. Java – Класс ArrayList
- 31.10. Java – Класс HashSet
- 31.11. Java – Класс LinkedHashSet
- 31.12. Java – Класс TreeSet
- 31.13. Java – Класс HashMap
- 31.14. Java – Класс TreeMap
- 31.15. Java – Класс WeakHashMap
- 31.16. Java – Класс LinkedHashMap
- 31.17. Java – Класс IdentityHashMap
- 31.18. Java – Алгоритмы Collection
- 31.19. Java – Iterator и ListIterator
- 31.20. Java – Comparator
- 32. Java – Дженерики
- 33. Java – Сериализация
- 34. Java – Сеть
- 34.1. Java – Обработка URL
- 35. Java – Отправка Email
- 36. Java – Многопоточность
- 36.1. Java – Синхронизация потоков
- 36.2. Java – Межпоточная связь
- 36.3. Java – Взаимная блокировка потоков
- 36.4. Java – Управление потоками
- 37. Java – Основы работы с апплетами
- 38. Java – Javadoc
Объявления вложенных классов
Класс можно объявить в области другого класса. Такой класс называется вложенным классом. Вложенные классы считаются в пределах область включаемого класса и доступны для использования в рамках этого область. Для обращения ко вложенному классу из области, отличной от непосредственно включающей его области, следует использовать полное имя.
В следующем примере показано, как объявить вложенные классы.
// nested_class_declarations.cpp class BufferedIO < public: enum IOError < None, Access, General >; // Declare nested class BufferedInput. class BufferedInput < public: int read(); int good() < return _inputerror == None; >private: IOError _inputerror; >; // Declare nested class BufferedOutput. class BufferedOutput < // Member list >; >; int main()
BufferedIO::BufferedInput и BufferedIO::BufferedOutput объявляются в BufferedIO пределах . Эти имена классов не видимы за пределами области класса BufferedIO . Однако объект типа BufferedIO не содержит объекты типа BufferedInput или BufferedOutput .
Вложенные классы могут непосредственно использовать имена, имена типов, имена статических членов и перечислители только из включающего класса. Для использования имен других членов класса необходимо использовать указатели, ссылки или имена объектов.
В предыдущем примере BufferedIO к перечислению IOError можно получить доступ непосредственно с помощью функций-членов во вложенных классах BufferedIO::BufferedInput или BufferedIO::BufferedOutput , как показано в функции good .
Вложенные классы объявляют только типы в пределах области класса. Они не создают объекты по вложенном классе. В предыдущем примере объявляется два вложенных класса, но не объявляются объекты этих типов классов.
Исключением из видимости области объявления вложенного класса является объявление имени типа вместе с опережающим объявлением. В этом случае имя класса, объявленное с помощью опережающего объявления, видимо за пределами включающего класса, при этом область определена как наименьшая включающая область вне класса. Например:
// nested_class_declarations_2.cpp class C < public: typedef class U u_t; // class U visible outside class C scope typedef class V <>v_t; // class V not visible outside class C >; int main() < // okay, forward declaration used above so file scope is used U* pu; // error, type name only exists in class C scope u_t* pu2; // C2065 // error, class defined above so class C scope V* pv; // C2065 // okay, fully qualified name C::V* pv2; >
Права доступа во вложенных классах
Вложение класса в другой класс не предоставляет особые права доступа к функциям-членам вложенного класса. Аналогичным образом, функции-члены включающего класса не имеют особых прав доступа к членам вложенного класса.
Функции-члены во вложенных классах
Функции-члены, объявленные во вложенных классах, могут быть определены в области файла. Предыдущий пример можно было бы записать следующим образом:
// member_functions_in_nested_classes.cpp class BufferedIO < public: enum IOError < None, Access, General >; class BufferedInput < public: int read(); // Declare but do not define member int good(); // functions read and good. private: IOError _inputerror; >; class BufferedOutput < // Member list. >; >; // Define member functions read and good in // file scope. int BufferedIO::BufferedInput::read() < return(1); >int BufferedIO::BufferedInput::good() < return _inputerror == None; >int main()
В предыдущем примере синтаксис с полным именем типа используется для объявления имени функции. Объявление:
BufferedIO::BufferedInput::read()
означает » read функция, являющаяся членом BufferedInput класса, который находится в область BufferedIO класса». Так как в этом объявлении используется синтаксис имени квалифицированного типа, возможны конструкции следующей формы:
typedef BufferedIO::BufferedInput BIO_INPUT; int BIO_INPUT::read()
Предыдущее объявление эквивалентно предыдущему, но вместо имен классов используется typedef имя.
Дружественные функции во вложенных классах
Считается, что дружественные функции, объявленные во вложенном классе, находятся в области вложенного, а не включающего класса. Поэтому дружественные функции не получают особых прав доступа к членам или функциям-членам включающего класса. Если требуется использовать имя, объявленное во вложенном классе, в дружественной функции, и дружественная функция определена в области видимости файла, используйте полные имена типов, как показано ниже.
// friend_functions_and_nested_classes.cpp #include enum < sizeOfMessage = 255 >; char *rgszMessage[sizeOfMessage]; class BufferedIO < public: class BufferedInput < public: friend int GetExtendedErrorStatus(); static char *message; static int messageSize; int iMsgNo; >; >; char *BufferedIO::BufferedInput::message; int BufferedIO::BufferedInput::messageSize; int GetExtendedErrorStatus() < int iMsgNo = 1; // assign arbitrary value as message number strcpy_s( BufferedIO::BufferedInput::message, BufferedIO::BufferedInput::messageSize, rgszMessage[iMsgNo] ); return iMsgNo; >int main()
В следующем коде показана функция GetExtendedErrorStatus , объявленная в качестве дружественной функции. В функции, определенной в области видимости файла, сообщение копируется из статического массива в член класса. Обратите внимание, что для оптимальной реализации функции GetExtendedErrorStatus рекомендуется объявить ее следующим образом.
int GetExtendedErrorStatus( char *message )
В предыдущем интерфейсе несколько классов могут использовать службы этой функции, передав адрес памяти, в которую требуется скопировать сообщение об ошибке.