Внутренний класс Java и статический вложенный класс

StackOverflow https://stackoverflow.com/questions/70324

Вопрос

В чем основное различие между внутренним классом и статическим вложенным классом в Java?Играет ли дизайн / реализация какую-то роль в выборе одного из них?

Это было полезно?

Решение

Из самого Учебное пособие по Java:

Вложенные классы делятся на две категории:статический и нестатический.Вложенные классы, которые объявлены статическими, называются просто статическими вложенными классами.Нестатические вложенные классы называются внутренними классами.

Доступ к статическим вложенным классам осуществляется с использованием заключающего их имени класса:

OuterClass.StaticNestedClass

Например, чтобы создать объект для статического вложенного класса, используйте этот синтаксис:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();

Объекты, являющиеся экземплярами внутреннего класса, существуют внутри экземпляра внешнего класса.Рассмотрим следующие классы:

class OuterClass {
    ...
    class InnerClass {
        ...
    }
}

Экземпляр InnerClass может существовать только внутри экземпляра OuterClass и имеет прямой доступ к методам и полям заключающего его экземпляра.

Чтобы создать экземпляр внутреннего класса, вы должны сначала создать экземпляр внешнего класса.Затем создайте внутренний объект внутри внешнего объекта с помощью этого синтаксиса:

OuterClass.InnerClass innerObject = outerObject.new InnerClass();

видишь: Учебное пособие по Java - Вложенные классы

Для полноты картины обратите внимание, что существует также такая вещь, как внутренний класс без заключающий экземпляр:

class A {
  int t() { return 1; }
  static A a =  new A() { int t() { return 2; } };
}

Здесь, new A() { ... } является внутренний класс, определенный в статическом контексте и не имеет заключающего экземпляра.

Другие советы

Тот Самый В руководстве по Java говорится:

Терминология:Вложенные классы делятся на две категории:статические и нестатические.Вложенные классы, которые объявлены статическими, называются просто статические вложенные классы.Нестатические вложенные классы называются внутренними классами.

В обиходе термины "вложенный" и "внутренний" используются большинством программистов взаимозаменяемо, но я буду использовать правильный термин "вложенный класс", который охватывает как внутренний, так и статический.

Классы могут быть вложенными ad infinitum, напримеркласс A может содержать класс B, который содержит класс C, который содержит класс D, и т.д.Однако более одного уровня вложенности классов встречается редко, поскольку это, как правило, плохой дизайн.

Есть три причины, по которым вы могли бы создать вложенный класс:

  • организация:иногда кажется наиболее разумным отсортировать класс по пространству имен другого класса, особенно когда он не будет использоваться ни в каком другом контексте
  • доступ:вложенные классы имеют специальный доступ к переменным / полям содержащих их классов (какие именно переменные / поля зависят от типа вложенного класса, внутреннего или статического).
  • удобство:необходимость создавать новый файл для каждого нового типа опять же вызывает беспокойство, особенно когда тип будет использоваться только в одном контексте

Есть такие четыре вида вложенных классов в Java.Вкратце, это:

  • статический класс:объявлен как статический член другого класса
  • внутренний класс:объявлен как экземпляр-член другого класса
  • локальный внутренний класс:объявленный внутри метода экземпляра другого класса
  • анонимный внутренний класс:похоже на локальный внутренний класс, но записано как выражение, которое возвращает одноразовый объект

Позвольте мне остановиться на этом более подробно.


Статические классы

Статические классы - самый простой вид для понимания, потому что они не имеют ничего общего с экземплярами содержащего их класса.

Статический класс - это класс, объявленный как статический член другого класса.Точно так же, как и другие статические члены, такой класс на самом деле является просто вспомогательным элементом, который использует содержащий класс в качестве своего пространства имен, например , класс Козел объявлен как статический член класса Носорог в упаковке Пицца известен под этим названием пицца.Носорог.Козел.

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

Честно говоря, статические классы - довольно бесполезная функция, потому что классы уже разделены на пространства имен пакетами.Единственная реальная мыслимая причина для создания статического класса заключается в том, что такой класс имеет доступ к частным статическим членам содержащего его класса, но я нахожу это довольно неубедительным оправданием существования функции статического класса.


Внутренние Классы

Внутренний класс - это класс, объявленный как нестатический член другого класса:

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

    private void jerry() {
        Goat g = new Goat();
    }
}

Как и в случае со статическим классом, внутренний класс называется квалифицируемым по содержащему его имени класса, пицца.Носорог.Козел, но внутри содержащего его класса он может быть известен под своим простым именем.Однако каждый экземпляр внутреннего класса привязан к определенному экземпляру содержащего его класса:выше, в Козел созданный в джерри, неявно привязан к Носорог экземпляр это в джерри.В противном случае мы создаем связанный Носорог экземпляр явный, когда мы создаем экземпляр Козел:

Rhino rhino = new Rhino();
Rhino.Goat goat = rhino.new Goat();

(Обратите внимание, что вы ссылаетесь на внутренний тип как просто Козел в странном новое синтаксис:Java выводит содержащий тип из носорог часть.И, да новый носорог.Козел () для меня это тоже имело бы больше смысла.)

Так что же это нам дает?Ну, внутренний экземпляр класса имеет доступ к элементам экземпляра содержащего его экземпляра класса.На эти входящие в комплект члены экземпляра ссылаются внутри внутреннего класса через только их простые названия, а не через это (это во внутреннем классе ссылается на экземпляр внутреннего класса, а не на связанный содержащий экземпляр класса):

public class Rhino {

    private String barry;

    public class Goat {
        public void colin() {
            System.out.println(barry);
        }
    }
}

Во внутреннем классе вы можете ссылаться на это содержащего класса как Носорог. это, и вы можете использовать это обращаться к его членам, например ,Носорог.это.Барри.


Локальные Внутренние Классы

Локальный внутренний класс - это класс, объявленный в теле метода.Такой класс известен только в содержащем его методе, поэтому его экземпляр может быть создан только с доступом к его членам в содержащем его методе.Выгода заключается в том, что локальный экземпляр внутреннего класса привязан к конечным локальным переменным содержащего его метода и может получить к ним доступ.Когда экземпляр использует конечный локальный файл содержащего его метода, переменная сохраняет значение, которое она имела во время создания экземпляра, даже если переменная вышла за пределы области видимости (фактически это грубая, ограниченная версия closures в Java).

Поскольку локальный внутренний класс не является ни членом класса, ни пакетом, он не объявляется с уровнем доступа.(Однако имейте в виду, что его собственные члены имеют уровни доступа, как в обычном классе.)

Если локальный внутренний класс объявлен в методе экземпляра, создание экземпляра внутреннего класса привязывается к экземпляру, хранящемуся в содержащем методе это во время создания экземпляра, и, таким образом, члены экземпляра содержащего класса доступны, как во внутреннем классе экземпляра.Локальный внутренний класс создается просто через его название, например , локальный внутренний класс Кошка создается как новый Кот(), не новый this.Cat (), как можно было ожидать.


Анонимные Внутренние Классы

Анонимный внутренний класс - это синтаксически удобный способ написания локального внутреннего класса.Чаще всего локальный внутренний класс создается не более одного раза при каждом запуске содержащего его метода.Было бы неплохо, если бы мы могли объединить локальное определение внутреннего класса и его единичное создание в одну удобную синтаксическую форму, и было бы также неплохо, если бы нам не нужно было придумывать имя для класса (чем меньше бесполезных имен содержит ваш код, тем лучше).Анонимный внутренний класс допускает обе эти вещи:

new *ParentClassName*(*constructorArgs*) {*members*}

Это выражение, возвращающее новый экземпляр безымянного класса, который расширяет Имя родительского класса.Вы не можете предоставить свой собственный конструктор;скорее, неявно предоставляется один, который просто вызывает суперконструктор, поэтому предоставленные аргументы должны соответствовать суперконструктору.(Если родительский файл содержит несколько конструкторов, вызывается “самый простой” из них, “simplest” определяется довольно сложным набором правил, не стоит утруждать себя подробным изучением - просто обратите внимание на то, что говорят вам NetBeans или Eclipse.)

В качестве альтернативы вы можете указать интерфейс для реализации:

new *InterfaceName*() {*members*}

Такое объявление создает новый экземпляр неназванного класса, который расширяет Object и реализует Имя интерфейса.Опять же, вы не можете предоставить свой собственный конструктор;в этом случае Java неявно предоставляет конструктор без аргументов, ничего не делающий (поэтому в этом случае никогда не будет аргументов конструктора).

Даже если вы не можете предоставить анонимному внутреннему классу конструктор, вы все равно можете выполнить любую настройку, которую захотите, используя блок инициализатора (блок {}, размещенный вне любого метода).

Имейте в виду, что анонимный внутренний класс - это просто менее гибкий способ создания локального внутреннего класса с одним экземпляром.Если вам нужен локальный внутренний класс, который реализует несколько интерфейсов или который реализует интерфейсы, расширяя какой-либо класс, отличный от Объект или который указывает свой собственный конструктор, вы застряли, создавая обычный именованный локальный внутренний класс.

Я не думаю, что реальная разница стала ясна из приведенных выше ответов.

Сначала нужно правильно сформулировать условия:

  • Вложенный класс - это класс, который содержится в другом классе на уровне исходного кода.
  • Он статичен, если вы объявляете его с помощью статический модификатор.
  • Нестатический вложенный класс называется внутренним классом.(Я остаюсь с нестатическим вложенным классом.)

Ответ Мартина пока верен.Однако на самом деле вопрос заключается в следующем:Какова цель объявления вложенного класса статическим или нет?

Вы используете статические вложенные классы если вы просто хотите сохранить свои классы вместе, если они тематически принадлежат друг другу или если вложенный класс используется исключительно во вложенном классе.Нет никакой семантической разницы между статическим вложенным классом и любым другим классом.

Нестатические вложенные классы это другой зверь.Подобно анонимным внутренним классам, такие вложенные классы на самом деле являются замыканиями.Это означает, что они захватывают окружающую их область видимости и заключающий их экземпляр и делают это доступным.Возможно, какой-нибудь пример прояснит это.Видите эту заглушку Контейнера:

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 с помощью следующего синтаксиса 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)

Для полноты картины:

Анонимный класс является идеальный пример нестатического вложенного класса, с которым просто не связано имя, и на него нельзя ссылаться позже.

Я думаю, что ни один из приведенных выше ответов не объясняет вам реальной разницы между вложенным классом и статическим вложенным классом с точки зрения дизайна приложения :

Обзор

Вложенный класс может быть нестатическим или статичным, и в каждом конкретном случае определен ли класс внутри другого класса. Вложенный класс должен существовать только для обслуживания заключающего его класса, если вложенный класс полезен другим классам (не только окружающим), он должен быть объявлен как класс верхнего уровня.

Разница

Нестатический вложенный класс :неявно связан с включающим экземпляром содержащего класса, это означает, что можно вызывать методы и получать доступ к переменным включающего экземпляра.Одним из распространенных применений нестатического вложенного класса является определение класса адаптера.

Статический Вложенный класс :не удается получить доступ к экземпляру заключающего класса и вызвать методы в нем, поэтому его следует использовать, когда вложенному классу не требуется доступ к экземпляру заключающего класса.Обычным использованием статического вложенного класса является реализация компонентов внешнего объекта.

Заключение

Таким образом, основное различие между ними с точки зрения дизайна заключается в следующем : нестатический вложенный класс может получить доступ к экземпляру класса контейнера, в то время как статический не может.

Проще говоря, нам нужны вложенные классы в первую очередь потому, что Java не предоставляет замыканий.

Вложенные классы - это классы, определенные внутри тела другого заключающего класса.Они бывают двух типов - статические и нестатические.

Они рассматриваются как члены заключающего класса, следовательно, вы можете указать любой из четырех спецификаторов доступа - private, package, protected, public.У нас нет такой роскоши с классами верхнего уровня, которые могут быть только объявлены public или пакет-частный.

Внутренние классы, также известные как классы без стека, имеют доступ к другим членам верхнего класса, даже если они объявлены закрытыми, в то время как статические вложенные классы не имеют доступа к другим членам верхнего класса.

public class OuterClass {
    public static class Inner1 {
    }
    public class Inner2 {
    }
}

Inner1 является нашим статическим внутренним классом и Inner2 это наш внутренний класс, который не является статичным.Ключевое различие между ними в том, что вы не можете создать Inner2 экземпляр без внешнего, где, поскольку вы можете создать Inner1 возражайте независимо.

Когда бы вы использовали Внутренний класс?

Подумайте о ситуации, когда 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();

Когда бы вы использовали статический Внутренний класс?

Вы бы определили статический внутренний класс, если бы знали, что он не имеет никакой связи с экземпляром заключающего класса / верхнего класса.Если ваш внутренний класс не использует методы или поля внешнего класса, это просто пустая трата места, поэтому сделайте его статичным.

Например, чтобы создать объект для статического вложенного класса, используйте этот синтаксис:

OuterClass.Inner1 nestedObject = new OuterClass.Inner1();

Преимущество статического вложенного класса заключается в том, что для работы ему не нужен объект содержащего его класса / top class.Это может помочь вам уменьшить количество объектов, создаваемых вашим приложением во время выполнения.

Я думаю, что конвенция, которой обычно придерживаются, такова:

  • статический класс внутри класса верхнего уровня находится вложенный класс
  • нестатический класс внутри класса верхнего уровня находится внутренний класс, который дополнительно имеет еще две формы:
    • местный класс - именованные классы, объявленные внутри блока, например, в теле метода или конструктора
    • анонимный класс - безымянные классы, экземпляры которых создаются в выражениях и операторах

Однако немногие другие указывает на воспоминания являются:

  • Классы верхнего уровня и статический вложенный класс семантически одинаковы, за исключением того, что в случае статического вложенного класса он может создавать статические ссылки на частные статические поля / методы своего внешнего [родительского] класса и наоборот.

  • Внутренние классы имеют доступ к переменным экземпляра окружающего экземпляра Внешнего [родительского] класса.Однако не все внутренние классы имеют заключающие экземпляры, например, внутренние классы в статических контекстах, такие как анонимный класс, используемый в блоке статического инициализатора, этого не делают.

  • Анонимный класс по умолчанию расширяет родительский класс или реализует родительский интерфейс, и нет дополнительного предложения для расширения какого-либо другого класса или реализации каких-либо дополнительных интерфейсов.Итак,

    • new YourClass(){}; означает class [Anonymous] extends YourClass {}
    • new YourInterface(){}; означает class [Anonymous] implements YourInterface {}

Я чувствую, что главный вопрос, который остается открытым, какой из них использовать и когда?Ну, это в основном зависит от того, с каким сценарием вы имеете дело, но чтение ответа, данного @jrudolph, может помочь вам принять какое-то решение.

Вот ключевые различия и сходства между внутренним классом Java и статическим вложенным классом.

Надеюсь, это поможет!

Внутренний класс

  • Может получить доступ к внешнему классу как инстанциальный, так и статический методы и поля
  • Связанный с экземпляром заключающего класса таким образом, для создания его экземпляра сначала требуется экземпляр внешнего класса (примечание новое ключевое слово place (место):

    Outerclass.InnerClass innerObject = outerObject.new Innerclass();
    
  • Не могу определите любой статические элементы сам по себе

  • Не могу иметь Класс или Интерфейс декларация

Статический вложенный класс

  • Не удается получить доступ внешний класс экземпляр методы или поля

  • Не связан ни с каким экземпляром заключающего класса Итак, чтобы создать его экземпляр:

    OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
    

Сходства

  • И то , и другое Внутренние классы может получить доступ даже частные поля и методы из внешний класс
  • Кроме того, Внешний класс иметь доступ к частные поля и методы из внутренние классы
  • Оба класса могут иметь модификатор частного, защищенного или общедоступного доступа

Зачем использовать вложенные классы?

Согласно документации Oracle, на это есть несколько причин (полная документация):

  • Это способ логической группировки классов, которые используются только в одном месте: Если класс полезен только для одного другого класса, то логично встроить его в этот класс и сохранить их вместе.Вложение таких "вспомогательных классов" делает их пакет более оптимизированным.

  • Это увеличивает инкапсуляцию: Рассмотрим два класса верхнего уровня, A и B, где B необходим доступ к членам A, которые в противном случае были бы объявлены закрытыми.Скрывая класс B внутри класса A, члены A могут быть объявлены закрытыми, и B может получить к ним доступ.Кроме того, сам B может быть скрыт от внешнего мира.

  • Это может привести к созданию более читаемого и поддерживаемого кода: Вложение небольших классов в классы верхнего уровня приближает код к месту его использования.

Вложенный класс:класс внутри класса

Типы:

  1. Статический вложенный класс
  2. Нестатический вложенный класс [Внутренний класс]

Разница:

Нестатический вложенный класс [Внутренний класс]

В нестатическом вложенном классе объект внутреннего класса существует внутри объекта внешнего класса.Таким образом, элемент данных внешнего класса доступен для внутреннего класса.Итак, чтобы создать объект внутреннего класса, мы должны сначала создать объект внешнего класса.

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 выполняется только после создания экземпляра класса (или, по крайней мере, только после первой ссылки на вложенный статический элемент), как вы можете убедиться сами комментируя и раскомментируя строки, на которые я ссылаюсь выше.То же самое не выполняется верно для 'outerItem'.

По крайней мере, это то, что я вижу в Java 6.0.

В случае создания экземпляра, экземпляр номера статический внутренний класс создается с обращением объект внешнего класса, в котором он определен.Это означает, что у него есть включающий экземпляр.Но экземпляр статического внутреннего класса создается со ссылкой на Внешний класс, а не с ссылкой на объект внешнего класса.Это означает, что не экземпляр заключением.

Например:

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….
  }
}

Я не думаю, что здесь есть что добавить, большинство ответов прекрасно объясняют различия между статическим вложенным классом и Внутренними классами.Однако рассмотрите следующую проблему при использовании вложенных классов по сравнению с внутренними классами.Как упоминалось в паре ответов, внутренние классы не могут быть созданы без экземпляра и их окружающего класса, что означает, что они УДЕРЖИВАТЬ a указатель к экземпляру их окружающего класса, что может привести к переполнению памяти или исключению переполнения стека из-за того, что 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 не может его собрать, и поскольку он ссылается (имеет указатель на) на внешний экземпляр, он тоже не собирается.Имея достаточное количество этих объектов в вашем проекте, вы можете столкнуться с нехваткой памяти.
По сравнению со статическими внутренними классами, которые не содержат указания на экземпляр внутреннего класса, потому что они связаны не с экземпляром, а с классом.Приведенная выше программа может печатать "Я уничтожен !" если вы сделаете Внутренний класс статичным и создадите экземпляр с помощью Outer.Inner i = new Outer.Inner();

Эти термины используются взаимозаменяемо.Если вы хотите быть действительно педантичным в этом вопросе, тогда вы мог бы определите "вложенный класс" для обозначения статического внутреннего класса, у которого нет заключающего в себе экземпляра.В коде у вас может быть что-то вроде этого:

public class Outer {
    public class Inner {}

    public static class Nested {}
}

Однако на самом деле это не очень общепринятое определение.

Вложенный класс - это очень общий термин:каждый класс, который не является верхним уровнем, является вложенным классом.Внутренний класс - это нестатический вложенный класс.Джозеф Дарси написал очень хорошее объяснение о Вложенные классы, Внутренние классы, классы-члены и Классы верхнего уровня.

Ммм...внутренний класс - ЭТО вложенный класс...вы имеете в виду анонимный класс и внутренний класс?

Редактировать:Если вы на самом деле имели в виду внутренний против анонимного...внутренний класс - это просто класс, определенный внутри класса, например:

public class A {
    public class B {
    }
}

Принимая во внимание, что анонимный класс является расширением класса, определенного анонимно, поэтому никакой фактический "класс не определен, как в:

public class A {
}

A anon = new A() { /* you could change behavior of A here */ };

Дальнейшее Редактирование:

Википедия утверждает, что есть разница на Java, но я работаю с Java уже 8 лет, и это первый раз, когда я слышу такое различие...не говоря уже о том, что там нет ссылок, подтверждающих это утверждение...суть в том, что внутренний класс - это класс, определенный внутри класса (статический или нет), а вложенный - это просто еще один термин, обозначающий то же самое.

Существует тонкая разница между статическим и нестатическим вложенным классом...в основном нестатические внутренние классы имеют неявный доступ к полям экземпляра и методам заключающего класса (таким образом, они не могут быть созданы в статическом контексте, это будет ошибкой компилятора).Статические вложенные классы, с другой стороны, не имеют неявного доступа к полям и методам экземпляра и МОГУТ быть созданы в статическом контексте.

Ориентирован на учащихся, которые являются новичками в 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?

Вложенный статический класс - это другой класс, который объявляется внутри класса как член и становится статическим.Вложенный статический класс также объявляется как член внешнего класса и может быть сделан закрытым, общедоступным или защищенным, как и любой другой член.Одно из основных преимуществ вложенного статического класса перед внутренним классом заключается в том, что экземпляр вложенного статического класса не привязан ни к какому заключающему экземпляру внешнего класса. Вам также не нужен какой-либо экземпляр внешнего класса для создания экземпляра вложенного статического класса в Java.

1) Он может получить доступ статические элементы данных внешнего класса, включая частный.
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 !!!

 }

Итак, подводя итог, статический класс не зависит от того, какой класс он содержит.Таким образом, они не могут учиться в обычном классе.(потому что обычному классу нужен экземпляр).

Когда мы объявляем статический класс-член внутри класса, он известен как вложенный класс верхнего уровня или статический вложенный класс.Это можно продемонстрировать, как показано ниже :

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);
     }

}

Проверка внешнего класса:

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()); 

    }

}

Я думаю, что ни один из приведенных выше ответов не дает вам реального примера разницы между вложенным классом и статическим вложенным классом с точки зрения дизайна приложения.И основное различие между статическим вложенным классом и внутренним классом заключается в возможности доступа к полю экземпляра внешнего класса.

Давайте взглянем на два следующих примера.

Статический гнездовой класс:Хорошим примером использования статических вложенных классов является builder pattern (https://dzone.com/articles/design-patterns-the-builder-pattern).

Для BankAccount мы используем статический вложенный класс, главным образом потому, что

  1. Статический экземпляр класса nest может быть создан раньше внешнего класса.

  2. В шаблоне builder builder - это вспомогательный класс, который используется для создания BankAccount.

  3. BankAccount.Конструктор связан только с BankAccount.Никакие другие классы не связаны с BankAccount.Конструктор.поэтому лучше организовать их вместе, не используя соглашение об именах.
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 мы используем внутренний класс, главным образом потому, что:

  1. Внутреннему классу MyAdapter необходимо получить доступ к члену внешнего класса.

  2. В приведенном примере 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
        }
    }
}

Прежде всего, не существует такого класса, который называется Static class.Статический модификатор, используемый с внутренним классом (называемый вложенным классом), говорит, что это статический член внешнего класса, что означает, что мы можем получить к нему доступ, как и с другими статическими членами, и без наличия какого-либо экземпляра внешнего класса.(Что изначально является преимуществом статики.)

Разница между использованием вложенного класса и обычного Внутреннего класса заключается в:

OuterClass.InnerClass inner = new OuterClass().new InnerClass();

Сначала мы можем создать экземпляр Outerclass, затем мы сможем получить доступ к Inner.

Но если класс вложен, то синтаксис такой:

OuterClass.InnerClass inner = new OuterClass.InnerClass();

Который использует статический синтаксис как обычную реализацию ключевого слова static.

Язык программирования Java позволяет вам определять класс внутри другого класса.Такой класс называется вложенным классом и проиллюстрирован здесь:

class OuterClass {
...
class NestedClass {
    ...
    }
}

Вложенные классы делятся на две категории:статический и нестатический.Вложенные классы, которые объявлены статическими, называются статическими вложенными классами.Нестатические вложенные классы называются внутренними классами.Одна вещь, которую мы должны иметь в виду, это то, что нестатические вложенные классы (внутренние классы) имеют доступ к другим членам заключающего класса, даже если они объявлены закрытыми.Статические вложенные классы имеют доступ к другим членам заключающего класса только в том случае, если они статичны.Он не может получить доступ к нестатическим членам внешнего класса.Как и в случае с методами и переменными класса, статический вложенный класс связан со своим внешним классом.Например, чтобы создать объект для статического вложенного класса, используйте этот синтаксис:

OuterClass.StaticNestedClass nestedObject =
 new OuterClass.StaticNestedClass(); 

Чтобы создать экземпляр внутреннего класса, вы должны сначала создать экземпляр внешнего класса.Затем создайте внутренний объект внутри внешнего объекта с помощью этого синтаксиса:

OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();

Почему мы используем вложенные классы

  1. Это способ логической группировки классов, которые используются только в одном месте.
  2. Это увеличивает инкапсуляцию.
  3. Это может привести к созданию более читаемого и сопровождаемого кода.

Источник: Учебные пособия по Java ™ - Вложенные классы

Разница в том, что объявление вложенного класса, которое также является статическим, может быть создано за пределами заключающего его класса.

Когда у вас есть объявление вложенного класса, это нет статический, также известный как внутренний класс, Java не позволит вам создать его экземпляр, кроме как через заключающий класс.Объект, созданный из внутреннего класса, связан с объектом, созданным из внешнего класса, поэтому внутренний класс может ссылаться на поля внешнего.

Но если он статический, то ссылка не существует, доступ к внешним полям невозможен (кроме как через обычную ссылку, как и к любому другому объекту), и поэтому вы можете создать экземпляр вложенного класса самостоятельно.

Я проиллюстрировал различные возможные сценарии корректности и ошибки, которые могут возникнуть в коде Java.

    class Outter1 {

        String OutStr;

        Outter1(String str) {
            OutStr = str;
        }

        public void NonStaticMethod(String st)  {

            String temp1 = "ashish";
            final String  tempFinal1 = "ashish"; 

            //  below static attribute not permitted
            // static String tempStatic1 = "static";    

            //  below static with final attribute not permitted         
            // static final String  tempStatic1 = "ashish";  

            // synchronized keyword is not permitted below          
            class localInnerNonStatic1 {            

                synchronized    public void innerMethod(String str11) {
                    str11 = temp1 +" sharma";
                    System.out.println("innerMethod ===> "+str11);
                }

                /* 
        //  static method with final not permitted
          public static void innerStaticMethod(String str11) { 

                    str11 = temp1 +" india";
                    System.out.println("innerMethod ===> "+str11);
                }*/
            }

            // static class not permitted below
            //  static class localInnerStatic1 {   }                            

        }

        public static  void StaticMethod(String st)     {

            String temp1 = "ashish";
            final String  tempFinal1 = "ashish"; 

            // static attribute not permitted below
            //static String tempStatic1 = "static";     

            //  static with final attribute not permitted below
            // static final String  tempStatic1 = "ashish";                         

            class localInnerNonStatic1 {
                public void innerMethod(String str11) {
                    str11 = temp1 +" sharma";
                    System.out.println("innerMethod ===> "+str11);
                }

                /*
    // static method with final not permitted
    public static void innerStaticMethod(String str11) {  
                    str11 = temp1 +" india";
                    System.out.println("innerMethod ===> "+str11);
                }*/
            }

            // static class not permitted below
            //  static class localInnerStatic1 {   }    

        }

        // synchronized keyword is not permitted
        static  class inner1 {          

            static String  temp1 = "ashish";
            String  tempNonStatic = "ashish";
            // class localInner1 {

            public void innerMethod(String str11) {
                str11 = temp1 +" sharma";
                str11 = str11+ tempNonStatic +" sharma";
                System.out.println("innerMethod ===> "+str11);
            }

            public static void innerStaticMethod(String str11) {
                //  error in below step
                str11 = temp1 +" india";    
                //str11 = str11+ tempNonStatic +" sharma";
                System.out.println("innerMethod ===> "+str11);
            }
            //}
        }

        //synchronized keyword is not permitted below
        class innerNonStatic1 {             

//This is important we have to keep final with static modifier in non
// static innerclass below
            static final String  temp1 = "ashish";  
            String  tempNonStatic = "ashish";
            // class localInner1 {

            synchronized    public void innerMethod(String str11) {
                tempNonStatic = tempNonStatic +" ...";
                str11 = temp1 +" sharma";
                str11 = str11+ tempNonStatic +" sharma";
                System.out.println("innerMethod ===> "+str11);
            }

            /*
            //  error in below step
            public static void innerStaticMethod(String str11) {   
                            //  error in below step
                            // str11 = tempNonStatic +" india";                     
                            str11 = temp1 +" india";
                            System.out.println("innerMethod ===> "+str11);
                        }*/
                    //}
                }
    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top