В руководстве по Java написано :
Терминология: вложенные классы
делится на две категории: статические
и не статично. Вложенные классы, которые
объявлены статическими просто называются
статические вложенные классы. Нестатические
вложенные классы называются внутренними
классы.
В общем, термины «вложенный» и «внутренний» используются большинством программистов взаимозаменяемо, но я буду использовать правильный термин «вложенный класс», который охватывает как внутренний, так и статический.
Классы могут быть вложенными до бесконечности , например класс A может содержать класс B, который содержит класс C, который содержит класс D и т. д. Тем не менее, более одного уровня вложенности классов встречается редко, так как обычно это плохой дизайн.
Существует три причины, по которым вы можете создать вложенный класс:
- организация: иногда кажется наиболее разумным отсортировать класс в пространство имен другого класса, особенно когда он не будет использоваться ни в каком другом контексте
- доступ: вложенные классы имеют специальный доступ к переменным / полям содержащихся в них классов (какие именно переменные / поля зависят от типа вложенного класса, внутреннего или статического).
- удобство: необходимость создавать новый файл для каждого нового типа утомительна, опять же, особенно когда тип будет использоваться только в одном контексте
В Java существует четырех видов вложенных классов . Вкратце, это:
- статический класс : объявлен как статический член другого класса
- внутренний класс : объявлен как член экземпляра другого класса
- локальный внутренний класс : объявлен внутри метода экземпляра другого класса
- анонимный внутренний класс : как локальный внутренний класс, но записан как выражение, которое возвращает одноразовый объект
Позвольте мне уточнить детали.
Статические классы
Статические классы легче всего понять, поскольку они не имеют ничего общего с экземплярами содержащего класса.
Статический класс - это класс, объявленный как статический член другого класса. Как и другие статические члены, такой класс на самом деле является просто вешалкой, который использует содержащий класс в качестве своего пространства имен, например, класс Goat , объявленный как статический член класса Rhino в упаковке pizza известен под названием pizza.Rhino.Goat .
package pizza;
public class Rhino {
...
public static class Goat {
...
}
}
Честно говоря, статические классы - довольно бесполезная функция, потому что классы уже разделены на пространства имен по пакетам. Единственная реальная причина создания статического класса заключается в том, что такой класс имеет доступ к закрытым статическим членам своего содержащего класса, но я считаю, что это довольно слабое оправдание существования функции статического класса.
Внутренние классы
Внутренний класс - это класс, объявленный как нестатический член другого класса:
package pizza;
public class Rhino {
public class Goat {
...
}
private void jerry() {
Goat g = new Goat();
}
}
Как и в случае статического класса, внутренний класс известен как квалифицированный по имени содержащего класса, pizza.Rhino.Goat , но внутри содержащего класса он может быть известен по его простому имени. Однако каждый экземпляр внутреннего класса привязан к конкретному экземпляру содержащего его класса: выше, Goat , созданный в jerry , неявно привязан к Rhino экземпляр это в Джерри . В противном случае мы делаем явным связанный экземпляр Rhino при создании экземпляра Goat :
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(Обратите внимание, что вы называете внутренний тип просто Goat в странном синтаксисе new : Java выводит содержащий тип из части rhino . И, да новый носорог. Goat () имел бы для меня больше смысла.)
Так что это нам дает? Ну, внутренний экземпляр класса имеет доступ к членам экземпляра содержащего экземпляра класса. Эти входящие в состав экземпляры-члены упоминаются внутри внутреннего класса через , только их простые имена, а не через this ( this во внутреннем классе ссылается на внутренний экземпляр класса, а не на связанный с ним экземпляр класса):
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
Во внутреннем классе вы можете ссылаться на этот содержащего класса как Rhino.this , и вы можете использовать this для ссылки на его члены , например Rhino.this.barry .
Местные внутренние классы
Локальный внутренний класс - это класс, объявленный в теле метода. Такой класс известен только в пределах его содержащего метода, поэтому он может быть создан только для экземпляра и получить доступ к его членам в пределах содержащего его метода. Преимущество заключается в том, что локальный экземпляр внутреннего класса привязан и может получить доступ к последним локальным переменным его содержащего метода. Когда экземпляр использует последний локальный элемент своего содержащего метода, переменная сохраняет значение, которое оно содержало во время создания экземпляра, даже если переменная вышла из области видимости (это фактически грубая ограниченная версия замыканий Java).
Поскольку локальный внутренний класс не является ни членом класса, ни пакета, он не объявляется с уровнем доступа. (Однако, имейте в виду, что его собственные члены имеют уровни доступа, как в обычном классе.)
Если в методе экземпляра объявлен локальный внутренний класс, создание экземпляра внутреннего класса привязывается к экземпляру, содержащемуся в содержащем методе this во время создания экземпляра, и поэтому содержащий его члены экземпляра класса доступны как в экземпляре внутреннего класса. Локальный внутренний класс создается просто через его имя, например, локальный внутренний класс Cat создается как new Cat () , а не new this.Cat (), как и следовало ожидать.
Анонимные внутренние классы
Анонимный внутренний класс - это синтаксически удобный способ написания локального внутреннего класса. Чаще всего локальный внутренний класс создается не более одного раза при каждом запуске содержащего его метода. Было бы хорошо, если бы мы могли объединить определение локального внутреннего класса и его единственное создание в одну удобную синтаксическую форму, и было бы также хорошо, если бы нам не приходилось придумывать имя для этого класса (тем меньше бесполезного имена ваш код содержит, тем лучше). Анонимный внутренний класс допускает обе эти вещи:
new *ParentClassName*(*constructorArgs*) {*members*}
Это выражение возвращает новый экземпляр безымянного класса, который расширяет ParentClassName . Вы не можете предоставить свой собственный конструктор; скорее, неявно предоставляется один, который просто вызывает супер-конструктор, поэтому предоставленные аргументы должны соответствовать супер-конструктору. (Если родительский элемент содержит несколько конструкторов, то называется «самый простой», «самый простой», что определяется довольно сложным набором правил, которые не стоит беспокоиться, чтобы изучать его подробно - просто обратите внимание на то, что говорят вам NetBeans или Eclipse.) 1160 *
В качестве альтернативы вы можете указать интерфейс для реализации:
new *InterfaceName*() {*members*}
Такое объявление создает новый экземпляр безымянного класса, который расширяет Object и реализует InterfaceName . Опять же, вы не можете предоставить свой собственный конструктор; в этом случае Java неявно предоставляет конструктор без аргументов, бездействия (поэтому в этом случае аргументы конструктора никогда не будут).
Даже если вы не можете присвоить анонимному внутреннему классу конструктор, вы все равно можете выполнить любую настройку, которую хотите, используя блок инициализатора (блок {}, помещенный вне любого метода).
BeПонятно, что анонимный внутренний класс - это просто менее гибкий способ создания локального внутреннего класса с одним экземпляром. Если вам нужен локальный внутренний класс, который реализует несколько интерфейсов или который реализует интерфейсы, расширяя какой-то класс, отличный от Object , или который указывает свой собственный конструктор, вы застряли, создав обычный локальный внутренний класс с именем.