Внутренний класс Java и статический вложенный класс
В чем основное различие между внутренним классом и статическим вложенным классом в Java? Играет ли дизайн / реализация роль в выборе одного из них?
32 ответа
Вложенные классы делятся на две категории: статические и нестатические. Вложенные классы, которые объявлены статическими, просто называются статическими вложенными классами. Нестатические вложенные классы называются внутренними классами.
Доступ к статическим вложенным классам осуществляется с использованием имени включающего класса:
OuterClass.StaticNestedClass
Например, чтобы создать объект для статического вложенного класса, используйте этот синтаксис:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
Объекты, которые являются экземплярами внутреннего класса, существуют в экземпляре внешнего класса. Рассмотрим следующие классы:
class OuterClass {
...
class InnerClass {
...
}
}
Экземпляр InnerClass может существовать только внутри экземпляра OuterClass и имеет прямой доступ к методам и полям его включающего экземпляра.
Чтобы создать экземпляр внутреннего класса, вы должны сначала создать экземпляр внешнего класса. Затем создайте внутренний объект во внешнем объекте с этим синтаксисом:
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
см. Java Tutorial - Вложенные классы
Для полноты заметим, что существует также такая вещь, как внутренний класс без включающего экземпляра:
class A {
int t() { return 1; }
static A a = new A() { int t() { return 2; } };
}
Вот, new A() { ... }
является внутренним классом, определенным в статическом контексте, и не имеет включающего экземпляра.
Терминология: вложенные классы делятся на две категории: статические и нестатические. Вложенные классы, которые объявлены статическими, просто называются статическими вложенными классами. Нестатические вложенные классы называются внутренними классами.
В общем, термины "вложенный" и "внутренний" используются большинством программистов взаимозаменяемо, но я буду использовать правильный термин "вложенный класс", который охватывает как внутренний, так и статический.
Классы могут быть вложенными до бесконечности, например, класс 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, но внутри содержащего класса он может быть известен по его простому имени. Однако каждый экземпляр внутреннего класса привязан к конкретному экземпляру его содержащего класса: выше, козел, созданный в jerry, неявно привязан к экземпляру Rhino this в jerry. В противном случае мы делаем явный связанный экземпляр Rhino при создании экземпляра Goat:
Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();
(Обратите внимание, что вы называете внутренний тип просто Goat в странном новом синтаксисе: Java выводит содержащий тип из части rhino. И да, новый rhino.Goat() также имел бы для меня больше смысла.)
Так что же это нам дает? Ну, внутренний экземпляр класса имеет доступ к членам экземпляра содержащего экземпляра класса. На эти члены вложенного экземпляра ссылаются внутри внутреннего класса только через их простые имена, а не через это (это во внутреннем классе относится к экземпляру внутреннего класса, а не к связанному экземпляру содержащего класса):
public class Rhino {
private String barry;
public class Goat {
public void colin() {
System.out.println(barry);
}
}
}
Во внутреннем классе вы можете ссылаться на этот содержащий класс как Rhino.this, и вы можете использовать это для ссылки на его члены, например, Rhino.this.barry.
Местные Внутренние Классы
Локальный внутренний класс - это класс, объявленный в теле метода. Такой класс известен только в пределах его содержащего метода, поэтому он может быть создан только для экземпляра и получить доступ к его членам в пределах содержащего его метода. Преимущество заключается в том, что экземпляр локального внутреннего класса привязан и может получить доступ к последним локальным переменным его содержащего метода. Когда экземпляр использует последний локальный элемент своего содержащего метода, переменная сохраняет значение, которое оно содержало во время создания экземпляра, даже если переменная вышла из области видимости (это фактически грубая, ограниченная версия замыканий Java).
Поскольку локальный внутренний класс не является ни членом класса, ни пакета, он не объявляется с уровнем доступа. (Однако, имейте в виду, что его собственные члены имеют уровни доступа, как в обычном классе.)
Если в методе экземпляра объявлен локальный внутренний класс, создание экземпляра внутреннего класса привязывается к экземпляру, содержащемуся в содержащем методе this во время создания экземпляра, и поэтому члены экземпляра содержащего класса доступны, как в экземпляре. внутренний класс. Локальный внутренний класс создается просто через его имя, например, локальный внутренний класс Cat создается как новый Cat (), а не новый this.Cat(), как вы могли ожидать.
Анонимные Внутренние Классы
Анонимный внутренний класс - это синтаксически удобный способ написания локального внутреннего класса. Чаще всего локальный внутренний класс создается не более одного раза при каждом запуске содержащего его метода. Тогда было бы неплохо, если бы мы могли объединить определение локального внутреннего класса и его единственное создание в одну удобную синтаксическую форму, и было бы также хорошо, если бы нам не приходилось придумывать имя для этого класса (тем меньше бесполезного имена ваш код содержит, тем лучше). Анонимный внутренний класс допускает обе эти вещи:
new *ParentClassName*(*constructorArgs*) {*members*}
Это выражение, возвращающее новый экземпляр безымянного класса, который расширяет ParentClassName. Вы не можете предоставить свой собственный конструктор; скорее, неявно предоставляется один, который просто вызывает супер-конструктор, поэтому предоставленные аргументы должны соответствовать супер-конструктору. (Если родительский объект содержит несколько конструкторов, то называется "самый простой", "самый простой", что определяется довольно сложным набором правил, которые не стоит беспокоиться, чтобы подробно их изучить - просто обратите внимание на то, что говорят вам NetBeans или Eclipse.)
Кроме того, вы можете указать интерфейс для реализации:
new *InterfaceName*() {*members*}
Такое объявление создает новый экземпляр безымянного класса, который расширяет Object и реализует InterfaceName. Опять же, вы не можете предоставить свой собственный конструктор; в этом случае Java неявно предоставляет конструктор без аргументов, бездействия (поэтому в этом случае никогда не будет аргументов конструктора).
Даже если вы не можете дать анонимному внутреннему классу конструктор, вы все равно можете выполнить любую настройку, которую хотите, используя блок инициализатора (блок {}, помещенный вне любого метода).
Имейте в виду, что анонимный внутренний класс - это просто менее гибкий способ создания локального внутреннего класса с одним экземпляром. Если вам нужен локальный внутренний класс, который реализует несколько интерфейсов или который реализует интерфейсы, расширяя какой-то класс, отличный от Object, или который задает свой собственный конструктор, вы застряли в создании обычного локального внутреннего класса с именем.
Я не думаю, что реальная разница стала ясна в ответах выше.
Сначала, чтобы получить правильные условия:
- Вложенный класс - это класс, который содержится в другом классе на уровне исходного кода.
- Это статично, если вы объявляете это с помощью статического модификатора.
- Нестатический вложенный класс называется внутренним классом. (Я остаюсь с нестатическим вложенным классом.)
Ответ Мартина пока прав. Однако актуальный вопрос таков: какова цель объявления вложенного класса статическим или нет?
Вы используете статические вложенные классы, если вы просто хотите сохранить свои классы вместе, если они тематически принадлежат друг другу или если вложенный класс используется исключительно во включающем классе. Нет никакой семантической разницы между статическим вложенным классом и любым другим классом.
Нестатические вложенные классы - это другой зверь. Подобно анонимным внутренним классам, такие вложенные классы на самом деле являются замыканиями. Это означает, что они фиксируют окружающий их объем и включающий их экземпляр и делают его доступным. Возможно, пример прояснит это. Посмотрите эту заглушку контейнера:
public class Container {
public class Item{
Object data;
public Container getContainer(){
return Container.this;
}
public Item(Object data) {
super();
this.data = data;
}
}
public static Item create(Object data){
// does not compile since no instance of Container is available
return new Item(data);
}
public Item createSubItem(Object data){
// compiles, since 'this' Container is available
return new Item(data);
}
}
В этом случае вы хотите иметь ссылку от дочернего элемента на родительский контейнер. Используя нестатический вложенный класс, это работает без какой-либо работы. Вы можете получить доступ к включающему экземпляру контейнера с помощью синтаксиса Container.this
,
Более жесткие объяснения следующие:
Если вы посмотрите на байт-коды Java, которые генерирует компилятор для (нестатического) вложенного класса, это может стать еще яснее:
// class version 49.0 (49)
// access flags 33
public class Container$Item {
// compiled from: Container.java
// access flags 1
public INNERCLASS Container$Item Container Item
// access flags 0
Object data
// access flags 4112
final Container this$0
// access flags 1
public getContainer() : Container
L0
LINENUMBER 7 L0
ALOAD 0: this
GETFIELD Container$Item.this$0 : Container
ARETURN
L1
LOCALVARIABLE this Container$Item L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 1
public <init>(Container,Object) : void
L0
LINENUMBER 12 L0
ALOAD 0: this
ALOAD 1
PUTFIELD Container$Item.this$0 : Container
L1
LINENUMBER 10 L1
ALOAD 0: this
INVOKESPECIAL Object.<init>() : void
L2
LINENUMBER 11 L2
ALOAD 0: this
ALOAD 2: data
PUTFIELD Container$Item.data : Object
RETURN
L3
LOCALVARIABLE this Container$Item L0 L3 0
LOCALVARIABLE data Object L0 L3 2
MAXSTACK = 2
MAXLOCALS = 3
}
Как видите, компилятор создает скрытое поле Container this$0
, Это устанавливается в конструкторе, который имеет дополнительный параметр типа Container для указания включающего экземпляра. Вы не можете видеть этот параметр в исходном коде, но компилятор неявно генерирует его для вложенного класса.
Пример Мартина
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
так будет скомпилировано для вызова чего-то вроде (в байт-кодах)
new InnerClass(outerObject)
Для полноты картины:
Анонимный класс является прекрасным примером нестатического вложенного класса, с которым просто не связано имя, и на который нельзя ссылаться позже.
Я думаю, что ни один из приведенных выше ответов не объясняет вам реальную разницу между вложенным классом и статическим вложенным классом с точки зрения разработки приложения:
OverView
Вложенный класс может быть нестатическим или статическим, и в каждом случае это класс, определенный в другом классе. Вложенный класс должен существовать только для обслуживания включающего класса, если вложенный класс полезен другими классами (не только включающими), должен быть объявлен как класс верхнего уровня.
разница
Нестатический вложенный класс: неявно связан с включающим экземпляром содержащего класса, это означает, что можно вызывать методы и обращаться к переменным включающего экземпляра. Одним из распространенных применений нестатического вложенного класса является определение класса Adapter.
Статический вложенный класс: не может получить доступ к экземпляру включающего класса и вызвать для него методы, поэтому его следует использовать, когда вложенный класс не требует доступа к экземпляру включающего класса. Обычное использование статического вложенного класса заключается в реализации компонентов внешнего объекта.
Заключение
Таким образом, основное различие между ними с точки зрения проектирования заключается в том, что нестатический вложенный класс может обращаться к экземпляру класса контейнера, а статический - нет.
Вот ключевые различия и сходства между внутренним классом Java и статическим вложенным классом.
Надеюсь, поможет!
Внутренний класс
- Может обращаться к внешнему классу как к экземпляру, так и к статическим методам и полям
Связанный с экземпляром включающего класса, для того, чтобы создать его экземпляр, сначала необходим экземпляр внешнего класса (обратите внимание на новое ключевое слово place):
Outerclass.InnerClass innerObject = outerObject.new Innerclass();
Самне может определять статические элементы
- Не может иметь объявление класса или интерфейса
Статический вложенный класс
Невозможно получить доступ к методам или полям экземпляра внешнего класса
Не связано ни с одним экземпляром включающего класса. Итак, чтобы создать его экземпляр:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
сходства
- Оба внутренних класса могут обращаться даже к закрытым полям и методам внешнего класса.
- Также Outer класс имеет доступ к закрытым полям и методам внутренних классов.
- Оба класса могут иметь закрытый, защищенный или общедоступный модификатор
Зачем использовать вложенные классы?
Согласно документации Oracle, есть несколько причин ( полная документация):
Это способ логически сгруппировать классы, которые используются только в одном месте: если класс полезен только для одного другого класса, то логично встроить его в этот класс и сохранить оба вместе. Вложение таких "вспомогательных классов" делает их пакет более упорядоченным.
Это увеличивает инкапсуляцию: рассмотрим два класса верхнего уровня, A и B, где B необходим доступ к членам A, которые в противном случае были бы объявлены закрытыми. Скрывая класс B в классе A, члены A могут быть объявлены частными, и B может получить к ним доступ. Кроме того, сам B может быть скрыт от внешнего мира.
Это может привести к более удобочитаемому и поддерживаемому коду: вложение небольших классов в классы верхнего уровня помещает код ближе к месту его использования.
Проще говоря, нам нужны вложенные классы прежде всего потому, что Java не обеспечивает замыкания.
Вложенные классы - это классы, определенные внутри тела другого включающего класса. Они бывают двух типов - статические и нестатические.
Они рассматриваются как члены окружающего класса, поэтому вы можете указать любой из четырех спецификаторов доступа - private, package, protected, public
, У нас нет этой роскоши с классами высшего уровня, которые могут быть объявлены только public
или пакет-частный.
Внутренние классы или классы, не являющиеся стеками, имеют доступ к другим членам высшего класса, даже если они объявлены закрытыми, в то время как статические вложенные классы не имеют доступа к другим членам высшего класса.
public class OuterClass {
public static class Inner1 {
}
public class Inner2 {
}
}
Inner1
наш статический внутренний класс и Inner2
это наш внутренний класс, который не является статичным. Основное различие между ними, вы не можете создать Inner2
экземпляр без внешнего, где, как вы можете создать Inner1
возражать независимо
Когда вы будете использовать Inner class?
Подумайте о ситуации, когда Class A
а также Class B
относятся к, Class B
нужен доступ Class A
члены и Class B
относится только к Class A
, Внутренние классы входят в картину.
Для создания экземпляра внутреннего класса вам необходимо создать экземпляр вашего внешнего класса.
OuterClass outer = new OuterClass();
OuterClass.Inner2 inner = outer.new Inner2();
или же
OuterClass.Inner2 inner = new OuterClass().new Inner2();
Когда бы вы использовали статический Inner класс?
Вы определяете статический внутренний класс, когда знаете, что он не имеет никакого отношения к экземпляру включающего класса / верхнего класса. Если ваш внутренний класс не использует методы или поля внешнего класса, это просто пустая трата пространства, поэтому сделайте его статичным.
Например, чтобы создать объект для статического вложенного класса, используйте этот синтаксис:
OuterClass.Inner1 nestedObject = new OuterClass.Inner1();
Преимущество статического вложенного класса в том, что ему не нужен объект содержащего класса / верхнего класса для работы. Это может помочь вам уменьшить количество объектов, создаваемых вашим приложением во время выполнения.
Я думаю, что соглашение, которое обычно соблюдается, таково:
- статический класс в классе верхнего уровня является вложенным классом
- Нестатический класс внутри класса верхнего уровня является внутренним классом, который дополнительно имеет еще две формы:
- локальный класс - именованные классы, объявленные внутри блока как тело метода или конструктора
- анонимный класс - безымянные классы, экземпляры которых создаются в выражениях и выражениях
Однако, несколько других моментов, которые следует помнить:
Классы верхнего уровня и статический вложенный класс семантически одинаковы, за исключением того, что в случае статического вложенного класса он может делать статическую ссылку на частные статические поля / методы своего класса Outer [parent] и наоборот.
Внутренние классы имеют доступ к переменным экземпляра окружающего экземпляра класса Outer [parent]. Однако не все внутренние классы имеют вложенные экземпляры, например, внутренние классы в статических контекстах, как анонимный класс, используемый в статическом блоке инициализатора, не имеют.
По умолчанию анонимный класс расширяет родительский класс или реализует родительский интерфейс, и больше нет условий для расширения какого-либо другого класса или реализации каких-либо дополнительных интерфейсов. Так,
new YourClass(){};
средстваclass [Anonymous] extends YourClass {}
new YourInterface(){};
средстваclass [Anonymous] implements YourInterface {}
Я чувствую, что большой вопрос, который остается открытым, какой использовать и когда? Ну, это в основном зависит от того, с каким сценарием вы имеете дело, но чтение ответа @jrudolph может помочь вам принять какое-то решение.
Вложенный класс: класс внутри класса
Типы:
- Статический вложенный класс
- Нестатический вложенный класс [Внутренний класс]
Разница:
Нестатический вложенный класс [Внутренний класс]
В нестатическом вложенном классе объект внутреннего класса существует внутри объекта внешнего класса. Так что член данных внешнего класса доступен для внутреннего класса. Таким образом, чтобы создать объект внутреннего класса, мы должны сначала создать объект внешнего класса.
outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass();
Статический вложенный класс
В статическом вложенном классе объект внутреннего класса не нуждается в объекте внешнего класса, потому что слово "статический" указывает на отсутствие необходимости создавать объект.
class outerclass A {
static class nestedclass B {
static int x = 10;
}
}
Если вы хотите получить доступ к x, напишите следующий внутренний метод
outerclass.nestedclass.x; i.e. System.out.prinltn( outerclass.nestedclass.x);
Экземпляр внутреннего класса создается при создании экземпляра внешнего класса. Поэтому члены и методы внутреннего класса имеют доступ к членам и методам экземпляра (объекта) внешнего класса. Когда экземпляр внешнего класса выходит из области видимости, также экземпляры внутреннего класса перестают существовать.
Статический вложенный класс не имеет конкретного экземпляра. Он просто загружается при первом использовании (как статические методы). Это полностью независимая сущность, чьи методы и переменные не имеют доступа к экземплярам внешнего класса.
Статические вложенные классы не связаны с внешним объектом, они быстрее и не занимают кучи / стека памяти, потому что нет необходимости создавать экземпляр такого класса. Поэтому практическое правило состоит в том, чтобы попытаться определить статический вложенный класс с максимально ограниченной областью действия (private >= class >= protected >= public), а затем преобразовать его во внутренний класс (удалив "статический" идентификатор) и ослабить сфера, если это действительно необходимо.
Есть тонкость в использовании вложенных статических классов, которые могут быть полезны в определенных ситуациях.
В то время как статические атрибуты создаются до того, как создается экземпляр класса через его конструктор, статические атрибуты внутри вложенных статических классов, похоже, не создаются, пока не будет вызван конструктор класса, или, по крайней мере, до тех пор, пока атрибуты не будут впервые упомянуты, даже если они помечены как "окончательные".
Рассмотрим этот пример:
public class C0 {
static C0 instance = null;
// Uncomment the following line and a null pointer exception will be
// generated before anything gets printed.
//public static final String outerItem = instance.makeString(98.6);
public C0() {
instance = this;
}
public String makeString(int i) {
return ((new Integer(i)).toString());
}
public String makeString(double d) {
return ((new Double(d)).toString());
}
public static final class nested {
public static final String innerItem = instance.makeString(42);
}
static public void main(String[] argv) {
System.out.println("start");
// Comment out this line and a null pointer exception will be
// generated after "start" prints and before the following
// try/catch block even gets entered.
new C0();
try {
System.out.println("retrieve item: " + nested.innerItem);
}
catch (Exception e) {
System.out.println("failed to retrieve item: " + e.toString());
}
System.out.println("finish");
}
}
Даже при том, что 'nested' и 'innerItem' оба объявлены как 'static final'. установка nested.innerItem не происходит до тех пор, пока не будет создан экземпляр класса (или, по крайней мере, до тех пор, пока в первый раз не будет указана ссылка на вложенный статический элемент), как вы можете убедиться сами, комментируя и раскомментируя строки, на которые я ссылаюсь, выше. То же самое не относится к "externalItem".
По крайней мере, это то, что я вижу в Java 6.0.
В случае создания экземпляра экземпляр нестатического внутреннего класса создается со ссылкой на объект внешнего класса, в котором он определен. Это значит, что есть включающий экземпляр. Но экземпляр статического внутреннего класса создается с ссылкой на класс Outer, а не с ссылкой на объект внешнего класса. Это означает, что он не имеет включающего экземпляра.
Например:
class A
{
class B
{
// static int x; not allowed here…..
}
static class C
{
static int x; // allowed here
}
}
class Test
{
public static void main(String… str)
{
A o=new A();
A.B obj1 =o.new B();//need of inclosing instance
A.C obj2 =new A.C();
// not need of reference of object of outer class….
}
}
Термины используются взаимозаменяемо. Если вы хотите быть по-настоящему педантичным, то вы можете определить "вложенный класс" для ссылки на статический внутренний класс, у которого нет включающего экземпляра. В коде у вас может быть что-то вроде этого:
public class Outer {
public class Inner {}
public static class Nested {}
}
Это не очень общепринятое определение, хотя.
Я не думаю, что здесь есть что добавить, большинство ответов прекрасно объясняют различия между статическим вложенным классом и внутренним классом. Однако при использовании вложенных классов и внутренних классов учтите следующую проблему. Как упомянуто в нескольких ответах, внутренние классы не могут быть созданы без экземпляра и экземпляра своего включающего класса, что означает, что они Удерживают указатель на экземпляр своего включающего класса, что может привести к переполнению памяти или исключению переполнения стека из-за того, что GC не сможет собирать окружающие классы, даже если они больше не используются. Чтобы сделать это понятным, проверьте следующий код:
public class Outer {
public class Inner {
}
public Inner inner(){
return new Inner();
}
@Override
protected void finalize() throws Throwable {
// as you know finalize is called by the garbage collector due to destroying an object instance
System.out.println("I am destroyed !");
}
}
public static void main(String arg[]) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
// out instance is no more used and should be garbage collected !!!
// However this will not happen as inner instance is still alive i.e used, not null !
// and outer will be kept in memory until inner is destroyed
outer = null;
//
// inner = null;
//kick out garbage collector
System.gc();
}
Если вы удалите комментарий // inner = null;
В программе появится надпись "Я уничтожен!", Но оставлять это в комментариях не удастся.
Причина в том, что на внутренний внутренний экземпляр все еще ссылаются, GC не может собрать его, и поскольку он ссылается (имеет указатель на) на внешний экземпляр, он также не собирается. Наличие достаточного количества этих объектов в вашем проекте и может не хватить памяти.
По сравнению со статическими внутренними классами, которые не содержат указателя на экземпляр внутреннего класса, потому что он не связан с экземпляром, а связан с классом. Приведенная выше программа может вывести "Я уничтожен!", Если вы сделаете класс Inner статическим и создадите его с помощью Outer.Inner i = new Outer.Inner();
Я думаю, что ни один из приведенных выше ответов не дает вам реального примера разницы между вложенным классом и статическим вложенным классом с точки зрения разработки приложения. И главное различие между статическим вложенным классом и внутренним классом заключается в возможности доступа к полю экземпляра внешнего класса.
Давайте посмотрим на два следующих примера.
Статический вложенный класс. Хорошим примером использования статических вложенных классов является шаблон компоновщика ( https://dzone.com/articles/design-patterns-the-builder-pattern).
Для BankAccount мы используем статический вложенный класс, главным образом потому, что
Статический экземпляр класса гнезда может быть создан перед внешним классом.
В шаблоне компоновщика компоновщик является вспомогательным классом, который используется для создания BankAccount.
- BankAccount.Builder связан только с BankAccount. Никакие другие классы не связаны с BankAccount.Builder. так что лучше организовать их вместе, не используя соглашение об именах.
public class BankAccount {
private long accountNumber;
private String owner;
...
public static class Builder {
private long accountNumber;
private String owner;
...
static public Builder(long accountNumber) {
this.accountNumber = accountNumber;
}
public Builder withOwner(String owner){
this.owner = owner;
return this;
}
...
public BankAccount build(){
BankAccount account = new BankAccount();
account.accountNumber = this.accountNumber;
account.owner = this.owner;
...
return account;
}
}
}
Внутренний класс. Обычно используются внутренние классы для определения обработчика событий. https://docs.oracle.com/javase/tutorial/uiswing/events/generalrules.html
Для MyClass мы используем внутренний класс, главным образом потому, что:
Внутренний класс MyAdapter должен получить доступ к внешнему члену класса.
В этом примере MyAdapter связан только с MyClass. Другие классы не связаны с MyAdapter. так что лучше организовать их вместе, не используя соглашение об именах
public class MyClass extends Applet {
...
someObject.addMouseListener(new MyAdapter());
...
class MyAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent e) {
...// Event listener implementation goes here...
...// change some outer class instance property depend on the event
}
}
}
Ммм... внутренний класс - это вложенный класс... ты имеешь в виду анонимный класс и внутренний класс?
Изменить: Если вы на самом деле имели в виду внутренний против анонимного... внутренний класс это просто класс, определенный в классе, например:
public class A {
public class B {
}
}
Принимая во внимание, что анонимный класс является расширением класса, определенного анонимно, поэтому фактический "класс" не определен, как в:
public class A {
}
A anon = new A() { /* you could change behavior of A here */ };
Дальнейшее редактирование:
Википедия утверждает, что в Java есть разница, но я работаю с Java уже 8 лет, и это первое, что я услышал о таком различии... не говоря уже о том, что там нет ссылок, подтверждающих заявление... line, внутренний класс - это класс, определенный внутри класса (статический или нет), а вложенный - это просто еще один термин, означающий то же самое.
Существует небольшая разница между статическим и нестатическим вложенным классом... в основном нестатические внутренние классы имеют неявный доступ к полям экземпляра и методам включающего класса (поэтому они не могут быть созданы в статическом контексте, это будет компилятор ошибка). Статические вложенные классы, с другой стороны, не имеют неявного доступа к полям и методам экземпляра и МОГУТ быть сконструированы в статическом контексте.
Вложенный класс - это очень общий термин: каждый класс, который не является верхним уровнем, является вложенным классом. Внутренний класс - это нестатический вложенный класс. Джозеф Дарси написал очень хорошее объяснение о классах Nested, Inner, Member и Top-Level.
Ориентация на учащихся, которые являются новичками в Java и / или во вложенных классах
Вложенные классы могут быть:
1. Статические вложенные классы.
2. Нестатические вложенные классы. (также известный как Внутренние классы) => Пожалуйста, помните это
1. Внутренние занятия
Пример:
class OuterClass {
/* some code here...*/
class InnerClass { }
/* some code here...*/
}
Внутренние классы - это подмножества вложенных классов:
- внутренний класс - это особый тип вложенного класса
- внутренние классы являются подмножествами вложенных классов
- Вы можете сказать, что внутренний класс также является вложенным классом, но вы НЕ можете сказать, что вложенный класс также является внутренним классом.
Специальность внутреннего класса:
- Экземпляр внутреннего класса имеет доступ ко всем членам внешнего класса, даже к тем, которые помечены как "частные".
2. Статические вложенные классы:
Пример:
class EnclosingClass {
static class Nested {
void someMethod() { System.out.println("hello SO"); }
}
}
Случай 1: создание статического вложенного класса из не включающего класса
class NonEnclosingClass {
public static void main(String[] args) {
/*instantiate the Nested class that is a static
member of the EnclosingClass class:
*/
EnclosingClass.Nested n = new EnclosingClass.Nested();
n.someMethod(); //prints out "hello"
}
}
Случай 2: создание статического вложенного класса из включающего класса
class EnclosingClass {
static class Nested {
void anotherMethod() { System.out.println("hi again"); }
}
public static void main(String[] args) {
//access enclosed class:
Nested n = new Nested();
n.anotherMethod(); //prints out "hi again"
}
}
Специальность статических классов:
- Статический внутренний класс будет иметь доступ только к статическим членам внешнего класса и не будет иметь доступа к нестатическим членам.
Заключение:
Вопрос: В чем основное различие между внутренним классом и статическим вложенным классом в Java?
Ответ: просто пройдите специфику каждого класса, упомянутого выше.
Внутренний класс и вложенный статический класс в Java - это классы, объявленные внутри другого класса, известного как класс верхнего уровня в Java. В терминологии Java, если вы объявляете вложенный класс статическим, он будет называться вложенным статическим классом в Java, в то время как нестатический вложенный класс просто называется внутренним классом.
Что такое внутренний класс в Java?
Любой класс, который не является верхним уровнем или объявлен внутри другого класса, известен как вложенный класс, и из этих вложенных классов класс, который объявлен нестатическим, известен как внутренний класс в Java. Есть три вида внутреннего класса в Java:
1) Локальный внутренний класс - объявляется внутри блока кода или метода.
2) Анонимный внутренний класс - это класс, который не имеет имени для ссылки и инициализируется в том же месте, где он создается.
3) Член внутреннего класса - объявлен как нестатический член внешнего класса.
public class InnerClassTest {
public static void main(String args[]) {
//creating local inner class inside method i.e. main()
class Local {
public void name() {
System.out.println("Example of Local class in Java");
}
}
//creating instance of local inner class
Local local = new Local();
local.name(); //calling method from local inner class
//Creating anonymous inner class in Java for implementing thread
Thread anonymous = new Thread(){
@Override
public void run(){
System.out.println("Anonymous class example in java");
}
};
anonymous.start();
//example of creating instance of inner class
InnerClassTest test = new InnerClassTest();
InnerClassTest.Inner inner = test.new Inner();
inner.name(); //calling method of inner class
}
//Creating Inner class in Java
private class Inner{
public void name(){
System.out.println("Inner class example in java");
}
}
}
Что такое вложенный статический класс в Java?
Вложенный статический класс - это другой класс, который объявлен внутри класса как член и сделан статическим. Вложенный статический класс также объявляется как член внешнего класса и может быть закрытым, открытым или защищенным, как любой другой член. Одним из основных преимуществ вложенного статического класса перед внутренним классом является то, что экземпляр вложенного статического класса не привязан ни к какому включающему экземпляру класса Outer. Вам также не нужен какой-либо экземпляр класса Outer для создания экземпляра вложенного статического класса в Java.
1) Он может получить доступ к статическим данным членов внешнего класса, включая private.
2) Статический вложенный класс не может получить доступ к нестатическому (экземпляру) элементу данных или методу.
public class NestedStaticExample {
public static void main(String args[]){
StaticNested nested = new StaticNested();
nested.name();
}
//static nested class in java
private static class StaticNested{
public void name(){
System.out.println("static nested class example in java");
}
}
}
Ссылка: внутренний класс и вложенный статический класс в Java с примером
Я думаю, что люди здесь должны заметить для Афиши, что Классический Гнездовой Класс - это только первый внутренний класс. Например:
public static class A {} //ERROR
public class A {
public class B {
public static class C {} //ERROR
}
}
public class A {
public static class B {} //COMPILE !!!
}
Итак, подведем итог: статический класс не зависит от того, какой класс он содержит. Таким образом, они не могут в обычном классе. (потому что нормальному классу нужен экземпляр).
Диаграмма
Основное различие между static nested
а также non-static nested
классы в том, что static nested
не имеет доступа к нестатическим членам внешнего класса
Когда мы объявляем статический класс-член внутри класса, он называется вложенным классом верхнего уровня или статическим вложенным классом. Это можно продемонстрировать, как показано ниже:
class Test{
private static int x = 1;
static class A{
private static int y = 2;
public static int getZ(){
return B.z+x;
}
}
static class B{
private static int z = 3;
public static int getY(){
return A.y;
}
}
}
class TestDemo{
public static void main(String[] args){
Test t = new Test();
System.out.println(Test.A.getZ());
System.out.println(Test.B.getY());
}
}
Когда мы объявляем нестатический класс-член внутри класса, он называется внутренним классом. Внутренний класс можно продемонстрировать, как показано ниже:
class Test{
private int i = 10;
class A{
private int i =20;
void display(){
int i = 30;
System.out.println(i);
System.out.println(this.i);
System.out.println(Test.this.i);
}
}
}
Ниже приведен пример static nested class
а также inner class
:
OuterClass.java
public class OuterClass {
private String someVariable = "Non Static";
private static String anotherStaticVariable = "Static";
OuterClass(){
}
//Nested classes are static
static class StaticNestedClass{
private static String privateStaticNestedClassVariable = "Private Static Nested Class Variable";
//can access private variables declared in the outer class
public static void getPrivateVariableofOuterClass(){
System.out.println(anotherStaticVariable);
}
}
//non static
class InnerClass{
//can access private variables of outer class
public String getPrivateNonStaticVariableOfOuterClass(){
return someVariable;
}
}
public static void accessStaticClass(){
//can access any variable declared inside the Static Nested Class
//even if it private
String var = OuterClass.StaticNestedClass.privateStaticNestedClassVariable;
System.out.println(var);
}
}
OuterClassTest:
public class OuterClassTest {
public static void main(String[] args) {
//access the Static Nested Class
OuterClass.StaticNestedClass.getPrivateVariableofOuterClass();
//test the private variable declared inside the static nested class
OuterClass.accessStaticClass();
/*
* Inner Class Test
* */
//Declaration
//first instantiate the outer class
OuterClass outerClass = new OuterClass();
//then instantiate the inner class
OuterClass.InnerClass innerClassExample = outerClass. new InnerClass();
//test the non static private variable
System.out.println(innerClassExample.getPrivateNonStaticVariableOfOuterClass());
}
}
Диаграмма
Разница между static nested
а также non-static nested
классы
- Статические вложенные классы не имеют прямого доступа к другим членам (нестатическим переменным и методам) включающего класса, поскольку, поскольку он является статическим, он должен обращаться к нестатическим членам своего включающего класса через объект. То есть он не может напрямую ссылаться на нестатические члены своего окружающего класса. Из-за этого ограничения статические вложенные классы используются редко.
- Нестатические вложенные классы (внутренние классы) имеют доступ ко всем членам (статическим и нестатическим переменным и методам, включая private) своего внешнего класса и могут ссылаться на них напрямую так же, как и другие нестатические члены внешнего класс сделать.
Узнайте больше здесь
Статические вложенные классы получают доступ к PRIVATE статическим переменным уровня класса класса, в котором они определены. Это может быть огромным с архитектурной точки зрения (например, шаблон Service Locator, использующий вложенные статические вспомогательные классы в службах), и может помочь OP понять, почему они существуют вместе с внутренние классы.
Язык программирования Java позволяет вам определять класс в другом классе. Такой класс называется вложенным классом и иллюстрируется здесь:
class OuterClass {
...
class NestedClass {
...
}
}
Вложенные классы делятся на две категории: статические и нестатические. Вложенные классы, которые объявлены статическими, называются статическими вложенными классами. Нестатические вложенные классы называются внутренними классами. Мы должны помнить одну вещь: нестатические вложенные классы (внутренние классы) имеют доступ к другим членам включающего класса, даже если они объявлены закрытыми. Статические вложенные классы имеют доступ к другим членам включающего класса, только если они являются статическими. Он не может получить доступ к нестатическим членам внешнего класса. Как и в случае методов и переменных класса, статический вложенный класс связан с его внешним классом. Например, чтобы создать объект для статического вложенного класса, используйте этот синтаксис:
OuterClass.StaticNestedClass nestedObject =
new OuterClass.StaticNestedClass();
Чтобы создать экземпляр внутреннего класса, вы должны сначала создать экземпляр внешнего класса. Затем создайте внутренний объект во внешнем объекте с этим синтаксисом:
OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();
Почему мы используем вложенные классы
- Это способ логической группировки классов, которые используются только в одном месте.
- Это увеличивает инкапсуляцию.
- Это может привести к более удобочитаемому и поддерживаемому коду.
Другой вариант использования вложенных классов, помимо уже упомянутых, - это когда у вложенного класса есть методы, которые должны быть доступны только из внешнего класса. Это возможно, потому что внешний класс имеет доступ к частным конструкторам, полям и методам вложенного класса.
В приведенном ниже примере
Bank
может выдать a, который имеет частный конструктор и может изменять лимит кредитной карты в соответствии с текущей политикой банка, используя частный
setLimit(...)
метод экземпляра. (Прямой доступ поля к переменной экземпляра
limit
также будет работать в этом случае). Из любого другого класса только общедоступные методы
Bank.CreditCard
доступны.
public class Bank {
// maximum limit as per current bank policy
// is subject to change
private int maxLimit = 7000;
// ------- PUBLIC METHODS ---------
public CreditCard issueCard(
final String firstName,
final String lastName
) {
final String number = this.generateNumber();
final int expiryDate = this.generateExpiryDate();
final int CVV = this.generateCVV();
return new CreditCard(firstName, lastName, number, expiryDate, CVV);
}
public boolean setLimit(
final CreditCard creditCard,
final int limit
) {
if (limit <= this.maxLimit) { // check against current bank policy limit
creditCard.setLimit(limit); // access private method Bank.CreditCard.setLimit(int)
return true;
}
return false;
}
// ------- PRIVATE METHODS ---------
private String generateNumber() {
return "1234-5678-9101-1123"; // the numbers should be unique for each card
}
private int generateExpiryDate() {
return 202405; // date is YYYY=2024, MM=05
}
private int generateCVV() {
return 123; // is in real-life less predictable
}
// ------- PUBLIC STATIC NESTED CLASS ---------
public static final class CreditCard {
private final String firstName;
private final String lastName;
private final String number;
private final int expiryDate;
private final int CVV;
private int balance;
private int limit = 100; // default limit
// the constructor is final but is accessible from outer class
private CreditCard(
final String firstName,
final String lastName,
final String number,
final int expiryDate,
final int CVV
) {
this.firstName = firstName;
this.lastName = lastName;
this.number = number;
this.expiryDate = expiryDate;
this.CVV = CVV;
}
// ------- PUBLIC METHODS ---------
public String getFirstName() {
return this.firstName;
}
public String getLastName() {
return this.lastName;
}
public String getNumber() {
return this.number;
}
public int getExpiryDate() {
return this.expiryDate;
}
// returns true if financial transaction is successful
// otherwise false
public boolean charge(final int amount) {
final int newBalance = this.balance - amount;
if (newBalance < -this.limit) {
return false;
}
this.balance = newBalance;
return true;
}
// ------- PRIVATE METHODS ---------
private int getCVV() {
return this.CVV;
}
private int getBalance() {
return this.balance;
}
private void setBalance(final int balance) {
this.balance = balance;
}
private int getLimit() {
return limit;
}
private void setLimit(final int limit) {
this.limit = limit;
}
}
}
Разница между статическим и нестатическим вложенным классом в Java
1) Вложенный статический класс не нуждается в ссылке на внешний класс, но нестатический вложенный класс или внутренний класс требует ссылки на внешний класс. Вы не можете создать экземпляр класса Inner без создания экземпляра класса Outer. Это, безусловно, самое важное, что нужно учитывать при создании статического или нестатического вложенного класса.
2) статический класс на самом деле является статическим членом класса и может использоваться в статическом контексте, например, в статическом методе или статическом блоке внешнего класса.
3) Другое различие между статическим и нестатическим вложенным классом заключается в том, что вы не можете получить доступ к нестатическим членам, например, к методу и полю непосредственно во вложенном статическом классе. Если вы это сделаете, вы получите ошибку типа "Нестатический член не может быть использован в статическом контексте". В то время как внутренний класс может получить доступ как к статическому, так и к нестатическому члену класса Outer.
public class OuterClass {
private static String message = "Hello JAVA";
// How to create instance of static and non static nested class
public static void main(String... args) {
// creating instance of nested Static class
InnerClassStatic printer = new InnerClassStatic();
//calling non static method of nested static class
printer.printMessage();
// creating instance of non static nested class or InnerClass class
// In order to create instance of InnerClass class you need an OuterClass class instance
OuterClass outerClass = new OuterClass(); //outerClass class instance for creating non static nested class
InnerClass innerClass = outerClass.new InnerClass();
innerClass.display(); //calling non static method of InnerClass class
// we can also combine above steps in one step to create instance of InnerClass class
InnerClass nonStaticIner = new OuterClass().new InnerClass();
nonStaticIner.display(); // similarly you can now call InnerClass class method
}
// Static nested class
private static class InnerClassStatic {
//Only static member of OuterClass class is directly accessible in nested static class
public void printMessage() {
// Compile time error if message field is not static
System.out.println("Message from nested static class : " + message);
}
}
//non static nested class - also called InnerClass class
private class InnerClass {
// Both static and non static member of OuterClass class is accessible in this InnerClass class
public void display() {
System.out.println(" Message from non static nested or InnerClass class : " + message);
}
}
}
Выход:
Message from nested static class : Hello JAVA
Message from non static nested or Inner class : Hello JAVA
Message from non static nested or Inner class : Hello JAVA
Вот и все о разнице между статическим и не статическим вложенным классом в Java. До сих пор мы касались только внутреннего класса члена и не обсуждали два других типа внутреннего класса, например, локальный класс и анонимный внутренний класс. В этом руководстве по Java мы увидели, что такое вложенный статический класс в Java, и как создать экземпляр как вложенного статического, так и нестатического класса в Java.
Таким образом, легко создать экземпляр вложенного статического класса, поскольку он не требует экземпляра класса Outer, в то время как нестатический вложенный класс, например, Inner class, всегда будет нуждаться в экземпляре класса Outer и не может существовать без класса Outer. Если вам нужно выбирать между статическим и не статическим классом, то предпочитайте статический вложенный класс, если вы можете использовать это.
Просто некоторые дополнения после того, как вы прочитали Java-учебник оракула по классам и объектам. Это может помочь, если вы знаете модель памяти Java для внутреннего класса и статического вложенного класса. Пожалуйста, прочитайте следующие три вопроса, чтобы понять, почему внутренний класс и статический вложенный класс спроектированы Java таким образом. И тогда вы можете знать, когда использовать внутренний класс, а когда использовать статический вложенный класс.
Как внутренний объект класса находится в памяти?
Управление памятью - внутренний статический класс в Java
Когда статический вложенный класс (и статические члены в нем) загружается в память?
Затем прочтите это, чтобы сравнить внутренний класс и статический вложенный класс.
В чем преимущество создания внутреннего класса как статического с Java?
Прежде всего, нет такого класса, называемого Static class. Использование модификатора Static с внутренним классом (называемым Nested Class) говорит о том, что он является статическим членом Outer Class, что означает, что мы можем получить к нему доступ, как и с другими статическими членами без экземпляр Внешнего класса. (Что является преимуществом статического изначально.)
Разница между использованием вложенного класса и обычного внутреннего класса заключается в следующем:
OuterClass.InnerClass inner = new OuterClass().new InnerClass();
Сначала мы можем создать экземпляр Outer class, затем мы можем получить доступ к Inner.
Но если класс является вложенным, то синтаксис:
OuterClass.InnerClass inner = new OuterClass.InnerClass();
Который использует статический синтаксис в качестве обычной реализации статического ключевого слова.
Это довольно просто, сравнивая статические локальные классы и нестатические внутренние классы.
Различия:
Статический локальный класс:
может получить доступ только к статическим членам внешнего класса.
Не может быть статических инициализаторов.
Невозможно получить доступ непосредственно извне функции, в которой она объявлена.