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
Как называется класс объявленный внутри метода java
Классы могут быть вложенными (nested), то есть могут быть определены внутри других классов. Частным случаем вложенных классов являются внутренние классы (inner class). Например, имеется класс Person, внутри которого определен класс Account:
public class Program < public static void main(String[] args) < Person tom = new Person("Tom", "qwerty"); tom.displayPerson(); tom.account.displayAccount(); >> class Person < private String name; Account account; Person(String name, String password)< this.name = name; account = new Account(password); >public void displayPerson() < System.out.printf("Person \t Name: %s \t Password: %s \n", name, account.password); >public class Account < private String password; Account(String pass)< this.password = pass; >void displayAccount() < System.out.printf("Account Login: %s \t Password: %s \n", Person.this.name, password); >> >
Внутренний класс ведет себя как обычный класс за тем исключением, что его объекты могут быть созданы только внутри внешнего класса.
Внутренний класс имеет доступ ко всем полям внешнего класса, в том числе закрытым с помощью модификатора private. Аналогично внешний класс имеет доступ ко всем членам внутреннего класса, в том числе к полям и методам с модификатором private.
Ссылку на объект внешнего класса из внутреннего класса можно получить с помощью выражения Внешний_класс.this , например, Person.this .
Объекты внутренних классов могут быть созданы только в том классе, в котором внутренние классы опеределены. В других внешних классах объекты внутреннего класса создать нельзя.
Еще одной особенностью внутренних классов является то, что их можно объявить внутри любого контекста, в том числе внутри метода и даже в цикле:
public class Program < public static void main(String[] args) < Person tom = new Person("Tom"); tom.setAccount("qwerty"); >> class Person < private String name; Person(String name)< this.name = name; >public void setAccount (String password) < class Account< void display()< System.out.printf("Account Login: %s \t Password: %s \n", name, password); >> Account account = new Account(); account.display(); > >
Статические вложенные классы
Кроме внутренних классов также могут быть статические вложенные классы. Статические вложенные классы позволяют скрыть некоторую комплексную информацию внутри внешнего класса:
class Math < public static class Factorial< private int result; private int key; public Factorial(int number, int x)< result=number; key = x; >public int getResult() < return result; >public int getKey() < return key; >> public static Factorial getFactorial(int x) < int result=1; for (int i = 1; i return new Factorial(result, x); > >
Здесь определен вложенный класс для хранения данных о вычислении факториала. Основные действия выполняет метод getFactorial , который возвращает объект вложенного класса. И теперь используем классы в методе main:
public static void main(String[] args)
Локальный внутренний класс в Java
В Java, как и методы, переменные класса также могут иметь другой класс в качестве члена. Написание одного класса в другом разрешено в Java. Класс, написанный внутри, называется вложенным классом, а класс, который содержит внутренний класс, называется внешним классом.
Синтаксис
Ниже приведен синтаксис для написания вложенного класса. Здесь класс Outer_Demo является внешним классом, а класс Inner_Demo является вложенным классом.
class Outer_Demo < class Inner_Demo < >>
Вложенные классы делятся на два типа.
- Нестатические вложенные классы – это нестатические члены класса.
- Статические вложенные классы – это статические члены класса.
Внутренние классы (нестатические вложенные классы)
Внутренние классы – это механизм безопасности в Java. Мы знаем, что класс не может быть связан с модификатором доступа private, но если у нас есть класс как член другого класса, то внутренний класс можно сделать приватным. И это также используется для доступа к закрытым членам класса.
Внутренние классы бывают трех типов в зависимости от того, как и где вы их определяете. Они –
- Внутренний класс
- Метод локального внутреннего класса в Java
- Анонимный внутренний класс
Внутренний класс
Создать внутренний класс довольно просто. Вам просто нужно написать класс в классе. В отличие от класса, внутренний класс может быть закрытым, и как только вы объявите внутренний класс закрытым, к нему нельзя получить доступ из объекта вне класса.
Ниже приведена программа для создания внутреннего класса и доступа к нему. В данном примере мы делаем внутренний класс закрытым и обращаемся к нему через метод.
Пример
class Outer_Demo < int num; // inner class private class Inner_Demo < public void print() < System.out.println("This is an inner class"); >> // Accessing he inner class from the method within void display_Inner() < Inner_Demo inner = new Inner_Demo(); inner.print(); >> public class My_class < public static void main(String args[]) < // Instantiating the outer class Outer_Demo outer = new Outer_Demo(); // Accessing the display_Inner() method. outer.display_Inner(); >>
Здесь вы можете заметить, что Outer_Demo – это внешний класс, Inner_Demo – это внутренний класс, display_Inner() – это метод, внутри которого мы создаем экземпляр внутреннего класса, и этот метод вызывается из основного метода.
Если вы скомпилируете и запустите вышеуказанную программу, вы получите следующий результат.
Итог
This is an inner class.
Доступ
Как упоминалось ранее, внутренние классы также используются для доступа к закрытым членам класса. Предположим, у класса есть частные члены для доступа к ним. Напишите в нем внутренний класс, верните закрытые члены из метода внутреннего класса, скажем, getValue(), и, наконец, из другого класса (из которого вы хотите получить доступ к закрытым членам), вызовите метод getValue() внутреннего учебный класс.
Для создания экземпляра внутреннего класса сначала необходимо создать экземпляр внешнего класса. После этого, используя объект внешнего класса, ниже приведен способ, которым вы можете создать экземпляр внутреннего класса.
Outer_Demo outer = new Outer_Demo(); Outer_Demo.Inner_Demo inner = outer.new Inner_Demo();
Следующая программа показывает, как получить доступ к закрытым членам класса, используя внутренний класс.
Пример
class Outer_Demo < // private variable of the outer class private int num = 175; // inner class public class Inner_Demo < public int getNum() < System.out.println("This is the getnum method of the inner class"); return num; >> > public class My_class2 < public static void main(String args[]) < // Instantiating the outer class Outer_Demo outer = new Outer_Demo(); // Instantiating the inner class Outer_Demo.Inner_Demo inner = outer.new Inner_Demo(); System.out.println(inner.getNum()); >>
Если вы скомпилируете и запустите вышеуказанную программу, вы получите следующий результат.
Итог
This is the getnum method of the inner class: 175
Метод локального внутреннего класса
В Java мы можем написать класс внутри метода, и это будет локальный тип. Как и локальные переменные, область действия внутреннего класса ограничена внутри метода.
Внутренний класс, локальный для метода, может быть создан только внутри метода, в котором определен внутренний класс. Следующая программа показывает, как использовать метод локального внутреннего класса.
Пример
public class Outerclass < // instance method of the outer class void my_Method() < int num = 23; // method-local inner class class MethodInner_Demo < public void print() < System.out.println("This is method inner class "+num); >> // end of inner class // Accessing the inner class MethodInner_Demo inner = new MethodInner_Demo(); inner.print(); > public static void main(String args[]) < Outerclass outer = new Outerclass(); outer.my_Method(); >>
Если вы скомпилируете и запустите вышеуказанную программу, вы получите следующий результат.
Итог
This is method inner class 23
Анонимный внутренний класс
Внутренний класс, объявленный без имени класса, называется анонимным внутренним классом. В случае анонимных внутренних классов, мы объявляем и создаем их экземпляры одновременно. Обычно они используются всякий раз, когда вам необходимо переопределить метод класса или интерфейса. Синтаксис анонимного внутреннего класса следующий.
Синтаксис
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("This is an example of anonymous inner class"); >>; inner.mymethod(); > >
Если вы скомпилируете и запустите вышеуказанную программу, вы получите следующий результат.
Итог
This is an example of anonymous inner class
Таким же образом вы можете переопределить методы конкретного класса, а также интерфейс, используя анонимный внутренний класс.
Как аргумент
Как правило, если метод принимает объект интерфейса, абстрактный класс или конкретный класс, то мы можем реализовать интерфейс, расширить абстрактный класс и передать объект методу. Если это класс, то мы можем напрямую передать его в метод.
Но во всех трех случаях вы можете передать анонимный внутренний класс методу. Вот синтаксис передачи анонимного внутреннего класса в качестве аргумента метода.
obj.my_Method(new My_Class() < public void Do() < . . >>);
Следующая программа показывает, как передать анонимный внутренний класс в качестве аргумента метода.
Пример
// interface interface Message < String greet(); >public class My_class < // method which accepts the object of interface Message public void displayMessage(Message m) < System.out.println(m.greet() + ", This is an example of anonymous inner class as an argument"); >public static void main(String args[]) < // Instantiating the class My_class obj = new My_class(); // Passing an anonymous inner class as an argument obj.displayMessage(new Message() < public String greet() < return "Hello"; >>); > >
Если вы скомпилируете и запустите вышеуказанную программу, она даст вам следующий результат.
Итог
Hello, This is an example of anonymous inner class as an argument
Средняя оценка 4.3 / 5. Количество голосов: 12
Спасибо, помогите другим — напишите комментарий, добавьте информации к статье.
Или поделись статьей
Видим, что вы не нашли ответ на свой вопрос.
Помогите улучшить статью.
Напишите комментарий, что можно добавить к статье, какой информации не хватает.
Внутренние и вложенные классы 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 нужен доступ к членам, которые иначе были бы объявлены закрытыми.
Скрывая класс «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.
Из вложенного статического класса мы не имеем доступа к внешней не статической переменной внешнего класса.
Приведенный ниже код демонстрирует это:
/*
Учебный пример №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; // ошибка
>
Вывод: Мы не имеем доступа к не статическому полю внешнего класса, через статический контекст вложенного класса. Это подобно тому, как мы не имеем доступа из статического метода к нестатическим переменным класса. Точно также из статического вложенного класса мы не имеем доступа к нестатическим переменным внешнего класса.
Но мы имеем доступ к приватным статическим полям внешнего класса из вложенного статичного класса.
Приведенный ниже фрагмент кода демонстрирует это:
/*
Учебный пример №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
Все вопросы, комментарии, дополнения, критика приветствуются.
Продолжение следует…
Часть 2 >>>