1. введение
Этот учебник представляет собой быстрое и точное введение во вложенные классы на языке Java.
Проще говоря, Java позволяет нам определять классы внутри других классов. Вложенные классы позволяют нам логически группировать классы, которые используются только в одном месте, писать более читаемый и поддерживаемый код и увеличивать инкапсуляцию.
Прежде чем мы начнем, давайте рассмотрим несколько типов вложенных классов, доступных в языке:
- Статические вложенные классы
- Нестатические вложенные классы
- Местные классы
- Анонимные классы
В следующих разделах мы подробно обсудим каждый из них.
2. Статические Вложенные Классы
Вот несколько моментов, которые следует помнить о статических вложенных классах:
- Как и в случае со статическими членами, они принадлежат их заключающему классу, а не экземпляру класса
- Они могут иметь все типы модификаторов доступа в своем объявлении
- Они имеют доступ только к статическим членам в заключающем классе
- Они могут определять как статические, так и нестатические элементы
Давайте посмотрим, как мы можем объявить статический вложенный класс:
public class Enclosing { private static int x = 1; public static class StaticNested { private void run() { // method implementation } } @Test public void test() { Enclosing.StaticNested nested = new Enclosing.StaticNested(); nested.run(); } }
3. Нестатические Вложенные Классы
Далее, вот несколько быстрых моментов, которые следует помнить о нестатических вложенных классах:
- Они также называются внутренними классами
- Они могут иметь все типы модификаторов доступа в своем объявлении
- Так же, как переменные и методы экземпляра, внутренние классы связаны с экземпляром заключающего класса
- Они имеют доступ ко всем членам заключающего класса, независимо от того, являются ли они статическими или нестатическими
- Они могут определять только нестатические элементы
Вот как мы можем объявить внутренний класс:
public class Outer { public class Inner { // ... } }
Если мы объявляем вложенный класс с модификатором static , то он является статическим членом. В противном случае это внутренний класс. Несмотря на то, что синтаксически разница заключается только в одном ключевом слове (т. Е. static ), семантически существует огромная разница между этими типами вложенных классов. Экземпляры внутреннего класса привязаны к экземплярам окружающего класса, и поэтому они имеют доступ к своим членам. Мы должны знать об этой проблеме при выборе того, следует ли сделать вложенный класс внутренним.
Чтобы создать экземпляр внутреннего класса, мы должны сначала создать экземпляр его заключающего класса.
Давайте посмотрим, как мы можем это сделать:
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();
В следующих подразделах мы покажем некоторые специальные типы внутренних классов.
3.1. Локальные классы
Локальные классы – это особый тип внутренних классов, в которых класс определяется внутри метода или блока области.
Давайте рассмотрим несколько моментов, которые следует помнить об этом типе классов:
- Они не могут иметь модификаторы доступа в своем объявлении
- Они имеют доступ как к статическим, так и к нестатическим элементам в заключающем контексте
- Они могут определять только члены экземпляра
Вот краткий пример:
public class NewEnclosing { void run() { class Local { void run() { // method implementation } } Local local = new Local(); local.run(); } @Test public void test() { NewEnclosing newEnclosing = new NewEnclosing(); newEnclosing.run(); } }
3.2. Анонимные классы
Анонимные классы можно использовать для определения реализации интерфейса или абстрактного класса без необходимости создавать повторно используемую реализацию.
Давайте перечислим несколько моментов, которые следует помнить об анонимных классах:
- Они не могут иметь модификаторы доступа в своем объявлении
- Они имеют доступ как к статическим, так и к нестатическим элементам в заключающем контексте
- Они могут определять только члены экземпляра
- Это единственный тип вложенных классов, которые не могут определять конструкторы или расширять/реализовывать другие классы или интерфейсы
Чтобы определить анонимный класс, давайте сначала определим простой абстрактный класс:
abstract class SimpleAbstractClass { abstract void run(); }
Теперь давайте посмотрим, как мы можем определить анонимный класс:
public class AnonymousInnerUnitTest { @Test public void whenRunAnonymousClass_thenCorrect() { SimpleAbstractClass simpleAbstractClass = new SimpleAbstractClass() { void run() { // method implementation } }; simpleAbstractClass.run(); } }
Для получения более подробной информации мы можем найти полезным наш учебник по анонимным классам в Java .
4. Затенение
Объявление членов внутреннего класса затеняет членов заключающего класса , если они имеют одно и то же имя.
В этом случае ключевое слово this относится к экземплярам вложенного класса, а на члены внешнего класса можно ссылаться, используя имя внешнего класса.
Давайте рассмотрим краткий пример:
public class NewOuter { int a = 1; static int b = 2; public class InnerClass { int a = 3; static final int b = 4; public void run() { System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("NewOuterTest.this.a = " + NewOuter.this.a); System.out.println("NewOuterTest.b = " + NewOuter.b); System.out.println("NewOuterTest.this.b = " + NewOuter.this.b); } } @Test public void test() { NewOuter outer = new NewOuter(); NewOuter.InnerClass inner = outer.new InnerClass(); inner.run(); } }
5. Сериализация
Чтобы избежать java.io.NotSerializableException при попытке сериализации вложенного класса, мы должны:
- Объявите вложенный класс как статический
- Сделайте как вложенный класс, так и заключающий класс реализуемыми Сериализуемыми
6. Заключение
В этой статье мы рассмотрели, что такое вложенные классы и их различные типы. Мы также рассмотрели, как видимость полей и модификаторы доступа различаются между этими различными типами.
Как всегда, полную реализацию этого учебника можно найти на GitHub .