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


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


1452


источник


Ответы:


Из Учебник Java :

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

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

OuterClass.StaticNestedClass

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

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

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

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

Экземпляр Inner Class может существовать только в экземпляре Outer Class и имеет прямой доступ к методам и полям его вмещающего экземпляра.

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

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

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

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

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

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


1472



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

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

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

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

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

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

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

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

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


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

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

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

package pizza;

public class Rhino {

    ...

    public static class Goat {
        ...
    }
}

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


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

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

package pizza;

public class Rhino {

    public class Goat {
        ...
    }

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

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

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 ,


Местные внутренние классы

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

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

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


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

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

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

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

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

new *InterfaceName*() {*members*}

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

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

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


519



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

Прежде всего, чтобы получить правильные условия:

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

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

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

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

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)

Ради полноты:

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


124



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

OverView

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

разница

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

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

Вывод

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


81



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

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

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

Внутренние классы 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();

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


30



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

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

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

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

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

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

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

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


23



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

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

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

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

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

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

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

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

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

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

сходства

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

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

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

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

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

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


20



Nested class: class inside class

Types:

  1. Static nested class
  2. Non-static nested class [Inner class]

Difference:

Non-static nested class [Inner class]

In non-static nested class object of inner class exist within object of outer class. So that data member of outer class is accessible to inner class. So to create object of inner class we must create object of outer class first.

outerclass outerobject=new outerobject();
outerclass.innerclass innerobjcet=outerobject.new innerclass(); 

Static nested class

In static nested class object of inner class don't need object of outer class, because the word "static" indicate no need to create object.

class outerclass A {
    static class nestedclass B {
        static int x = 10;
    }
}

If you want to access x, then write the following inside method

  outerclass.nestedclass.x;  i.e. System.out.prinltn( outerclass.nestedclass.x);

12