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

1464

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

  • 54
    Ответ Джошуа Блоха находится в « Эффективном Java», читайте item 22 : Favor static member classes over non static
Теги:
inner-classes

27 ответов

1458
Лучший ответ

Из Учебник по Java:

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

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

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() { ... } - это внутренний класс, определенный в статическом контексте и не имеющий охватывающего экземпляра.

  • 113
    Помните, что вы также можете импортировать статический вложенный класс напрямую, т.е. вы можете сделать это (в верхней части файла): import OuterClass.StaticNestedClass; затем ссылаться на класс так же, как OuterClass.
  • 4
    У @Martin есть какое-то конкретное техническое имя для этой идиомы создания внутреннего класса OuterClass.InnerClass innerObject = outerObject.new InnerClass(); ?
Показать ещё 11 комментариев
513

Java-учебник говорит:

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

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

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

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

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

В Java есть четыре типа вложенного класса. Короче говоря, это:

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

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


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

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

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

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

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


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

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

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

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

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

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

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

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

public class Rhino {

    private String barry;

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

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


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

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

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

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


Анонимные внутренние классы

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

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

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

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

new *InterfaceName*() {*members*}

Такое объявление создает новый экземпляр неназванного класса, который расширяет Object и реализует InterfaceName. Опять же, вы не можете предоставить свой собственный конструктор; в этом случае Java неявно предоставляет конструктор no-arg, do-nothing (поэтому в этом случае никогда не будет аргументов конструктора).

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

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

  • 35
    Отличная история, спасибо. Хотя есть одна ошибка. Вы можете получить доступ к полям внешнего класса из внутреннего класса экземпляра с помощью Rhino.this.variableName.
  • 2
    Хотя вы не можете предоставить свой собственный конструктор для анонимных внутренних классов, вы можете использовать двойную инициализацию. c2.com/cgi/wiki?DoubleBraceInitialization
Показать ещё 7 комментариев
123

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

Сначала, чтобы получить правильные слова:

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

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

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

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

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)

Для полноты:

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

  • 16
    «Нет никакой семантической разницы между статическим вложенным классом и любым другим классом». За исключением того, что вложенный класс может видеть частные поля / методы родителя, а родительский класс может видеть частные поля / методы вложенного.
  • 0
    Не будет ли нестатический внутренний класс потенциально вызвать большие утечки памяти? Как каждый раз, когда вы создаете слушателя, вы создаете утечку?
Показать ещё 1 комментарий
89

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

OverView

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

Разница

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

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

Заключение

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

  • 0
    : из вашего заключения "пока статический не может", даже не статические экземпляры контейнера? Конечно?
  • 0
    Обычное использование статического вложенного класса - это шаблон проектирования ViewHolder в RecyclerView и ListView.
Показать ещё 1 комментарий
30

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

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

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

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

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

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

Когда вы будете использовать класс Inner?

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

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

  • 3
    Вы OuterClass.Inner2 inner = outer.new Inner2(); виду OuterClass.Inner2 inner = outer.new Inner2(); ?
  • 4
    static inner противоречие в терминах.
Показать ещё 1 комментарий
27

Я думаю, соглашение, которое обычно соблюдается, таково:

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

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

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

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

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

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

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

25

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

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

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

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

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

  • Нельзя иметь Класс или Объявление

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

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

  • Не связан с каким-либо экземпляром охватывающего класса. Чтобы его создать:

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

Сходства

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

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

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

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

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

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

15

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

Типы:

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

Difference:

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

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

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

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

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

Рассмотрим следующий пример:

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

Хотя "вложенные" и "innerItem" объявляются как "статические окончательные". Настройки of nested.innerItem не выполняется до тех пор, пока не будет создан экземпляр класса (или, по крайней мере, пока после первого вложенного статического элемента), как вы сами можете убедиться комментируя и раскомментируя строки, на которые я ссылаюсь выше. То же самое не выполняется true для 'externalItem'.

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

11

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

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

Статические вложенные классы не связаны с внешним объектом, они быстрее, и они не берут память кучи/стека, потому что не нужно создавать экземпляр такого класса. Поэтому эмпирическое правило состоит в том, чтобы попытаться определить статический вложенный класс с максимально возможной областью (private >= class >= protected >= public), а затем преобразовать его во внутренний класс (удалив "статический" идентификатор) и ослабить объем, если он действительно необходим.

  • 1
    Первое предложение неверно. Там нет такого понятия , как «экземпляр внутреннего класса», и экземпляры этого могут быть созданы в любое время после того, как внешний класс был инстанцированным. Второе предложение не следует из первого предложения.
10

Я не думаю, что здесь есть что добавить, большинство ответов прекрасно объясняют различия между статическим вложенным классом и внутренними классами. Однако рассмотрите следующую проблему при использовании вложенных классов и внутренних классов. Как упоминание в нескольких ответах, внутренние классы не могут быть созданы без экземпляра и их вмещающего класса, что означает, что они HOLD указатель к экземпляру их вмещающего класса, который может приводят к переполнению памяти или исключению из-за того, что 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();

10

В случае создания экземпляра экземпляр non статический внутренний класс создается со ссылкой на объект внешнего класса, в котором он определен. Эта означает, что у него есть инклюзивный экземпляр. Но экземпляр статического внутреннего класса создается со ссылкой на класс 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….
  }
}
  • 1
    «статический внутренний» является противоречием в терминах. Вложенный класс является статическим или внутренним.
8

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

public class Outer {
    public class Inner {}

    public static class Nested {}
}

Это не совсем общепринятое определение.

  • 2
    «статический внутренний» является противоречием в терминах.
  • 4
    Это не соглашение, которое определяет внутренний класс как нестатический вложенный класс, но JLS. docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3
Показать ещё 1 комментарий
8

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

7

Ориентационный ученик, который является новичком в Java и/или вложенными классами

Вложенные классы могут быть:
1. Статические вложенные классы.
 2. Нестатические вложенные классы. (также известный как Внутренние классы) = > Пожалуйста, запомните это


1.Интерьерные классы
Пример:

class OuterClass  {
/*  some code here...*/
     class InnerClass  {  }
/*  some code here...*/
}


Внутренние классы - это подмножества вложенных классов:

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

Специальность класса Inner:

  • экземпляр внутреннего класса имеет доступ ко всем членов внешнего класса, даже те, которые отмечены "private"


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?
Ответ: просто ознакомьтесь с особенностями каждого упомянутого выше класса.

7

Ммм... внутренний класс - это вложенный класс... ты имеешь в виду анонимный класс и внутренний класс?

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

public class A {
    public class B {
    }
}

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

public class A {
}

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

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

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

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

  • 3
    Согласно документации Java, существует разница между внутренним классом и статическим вложенным классом - статические вложенные классы не имеют ссылок на свой включающий класс и используются в основном для целей организации. Вы должны увидеть ответ Jegschemesch для более подробного описания.
  • 1
    Я думаю, что семантическая разница в основном историческая. Когда я писал компилятор C # -> Java 1.1, ссылка на язык Java была очень явной: вложенный класс является статическим, а внутренний класс - нет (и поэтому имеет этот $ 0). В любом случае, это сбивает с толку, и я рад, что это больше не проблема.
Показать ещё 1 комментарий
6

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

Что такое Inner Class в Java?

Любой класс, который не является верхним уровнем или объявлен внутри другого класса, известен как вложенный класс и из тех вложенных классов, класс, объявленные нестационарными, известен как класс Inner в Java. в Java есть три типа класса Inner:

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

Ссылка: Внутренний класс и вложенный Static Class в Java с примером

  • 2
    «Статический вложенный класс не может получить доступ к нестатическому (экземпляру) элементу данных или методу». неверно и вызывает путаницу . У них абсолютно есть доступ к частной информации об экземплярах - при условии, что они создают экземпляр для доступа к информации об этом экземпляре. У них нет включающего экземпляра, как у внутренних классов, но у них есть доступ к закрытым членам экземпляра включающего класса.
5

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

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

Я думаю, что люди здесь должны заметить Poster, что: Static Nest Class - это только первый внутренний класс. Например:

 public static class A {} //ERROR

 public class A {
     public class B {
         public static class C {} //ERROR
     }
 }

 public class A {
     public static class B {} //COMPILE !!!

 }

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

  • 2
    Это все нонсенс. Все это показывает, что внутренний класс не может содержать статический класс. Часть о «не зависит, какой класс он содержит» не имеет смысла, как и следующее предложение.
3

Ниже приведен пример 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()); 

    }

}
0

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

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

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

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

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

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

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

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

Источник: Учебные руководства Java - Вложенные классы

0

Разница между статическим и нестатическим вложенным классом в 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. Если вам нужно выбирать между статическим и не статическим классом, то предпочитайте статический вложенный класс, если вы можете использовать это.

0

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

Как внутренний объект класса находится в памяти?

Управление памятью - внутренний статический класс в Java

Когда в память загружается статический вложенный класс (и статические члены)?

Затем прочитайте это, чтобы сравнить внутренний класс и статический вложенный класс.

Какое преимущество создания внутреннего класса как статического с Java?

0

Я проиллюстрировал различные возможные правильные сценарии и ошибки, которые могут возникать в 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);
                        }*/
                    //}
                }
    }
  • 0
    Пожалуйста, посмотрите на форматирование вашего ответа.
  • 0
    Очевидно часть кода. А в случае, если вы не заметили: ваш пример кода очень сложен для чтения. Даже на моем огромном настольном мониторе появилась горизонтальная полоса прокрутки. Вы можете разместить свои комментарии выше или ниже того, что они комментируют, а не позади .
Показать ещё 1 комментарий
0

Отличие состоит в том, что вложенное объявление класса, которое также является статическим, может быть создано вне класса-оболочки.

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

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

  • 1
    Это неправда. Существует специальный синтаксис для создания внутреннего класса вне области действия включающего класса.
0

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

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

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

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

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

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

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

  • 1
    «... говорит, что это статический член Outer Class, что означает ....»: не является неправильным думать о статическом вложенном классе как о «классе-члене» Outer Class, но сходство со статическими полями и методы заканчиваются там. Статический вложенный класс не «принадлежит» внешнему классу. Практически во всех случаях, когда это имеет значение, статический вложенный класс - это отдельно стоящий класс верхнего уровня, определение класса которого было вложено в определение внешнего класса для удобства упаковки (и, надеюсь, потому что существует логическая связь между вложенным классом и внешним классом). ... хотя не должно быть одного).
  • 1
    «статический внутренний» является противоречием в терминах. Статические классы существуют на первом уровне вложенности и по определению не являются внутренними классами. Очень смущенный.
-3

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

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

Вы также не можете получить доступ к статическим свойствам класса из внешнего класса - как вы могли получить доступ к тому, что потенциально не было создано?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню