Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Модуль 8. Урок 1. Nested (static) классы в Java. Введение в Java

Видео может быть заблокировано из-за расширений браузера. В статье вы найдете решение этой проблемы.

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

  • Static Nested classes(статические вложенные классы)
  • Nested Inner classes (вложенные внутренние классы)
  • Method Local Inner classes (внутренние классы в локальном методе)
  • Anonymous Inner classes (анонимные классы)

Классификация Nested классов

Для чего они нужны?

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


В данном уроке будут рассмотрены только Static Nested classes (статические вложенные классы).

Static Nested classes используются для описания типа, который не должен зависеть от конкретного экземпляра внешнего класса, в который он вложен. Например, билдер — собирает очередной экземпляр внешнего класса и никак с ним больше не взаимодействует.

Как его обозначить в коде?

Static Nested class объявляется как и любой класс, НО внутри тела класса с ключевым словом static. Имена вложенных классов не имеют особых правил.

Static Nested class не имеет привязки к объекту внешнего класса (не хранит в себе ссылку на конкретный экземпляр внешнего класса).

Вложенный класс (Static Nested) имеет доступ к статическим членам своего внешнего класса, в том числе и к закрытым (private), даже без упоминания имени класса.

К НЕстатическим членам внешнего класса Nested(static) класс не имеет прямого доступа. Но такой доступ возможен через объект (экземпляр) внешнего класса, в том числе к закрытым (private) членам внешнего класса.

Вот код-пример:

/** Перед запуском этого кода — изучите логику кода.
 * И не стесняйтесь экспериментировать с кодом!
 */

public class External {
    private static int instanceCounter;
    private int a;
    int b;

    public External(int a, int b) {
        this.a = a;
        this.b = b;
        instanceCounter++;
    }

    //  private // Смело экспериментируйте!
    static void messenger(String incomeMessage) {
        String someMessage = "No message!";
        if (incomeMessage != null && !incomeMessage.isEmpty()) {
            someMessage = incomeMessage;
        }
        System.out.printf("The static method 'messenger()' of the outer class. Message: "
                        + "\"%s\"\n\n",
                someMessage);
    }

    // Все, что нам нужно от объектов External.
    String sum() {
        int result = a + b;
        return String.format("The non-static method 'sum()' of the outer class. %d + %d = %d",
                a,
                b,
                result);
    }

    static class NestedFoo {

        static String getInstanceCounter() {
            // Свободный доступ к статическому полю внешнего класса:
            return instanceCounter
                    + " (the information is provided by a static method 'getInstanceCounter()' "
                    + "from an inner class.)";
        }

        void methodFoExperiments() {
            System.out.println("'methodFoExperiments()' method in inner class.");

            // a = 5; // Ошибка! Ответьте себе почему?!
            // b = 5; // Ошибка! Т.к. "а" не статическое поле.

            // Свободный доступ к статическому методу внешнего класса,
            // даже без упоминания имени внешнего класса или его объектов:
            messenger("ПРЯМОЙ вызов 'messenger()' из 'methodFoExperiments()'");
            // Или с упоминанием его имени:
            External.messenger("ЧЕРЕЗ ИМЯ КЛАССА вызов 'messenger()' из "
                    + "'methodFoExperiments()'");

            // Создадим объект внешнего класса с псевдослучайными значениями полей:
            External instanceInInner = new External(10, 10);
            // теперь пощупаем эти поля через имя объекта:
            instanceInInner.a = 5; // Доступ к private  полю через объект.
            instanceInInner.b = 5; // Доступ к default (package visible) полю через объект.

            System.out.println(instanceInInner.sum());

            messenger("After second instantiation in inner class. Counter = "
                    + External.NestedFoo.getInstanceCounter());
        }
    }
}

// Где-то в этом же пакете класс:
class SomeMainClass {
    public static void main(String[] args) {
        // Потрогаем статический метод внешнего класса и статический метод статического внутреннего
        // класса:
        External.messenger("Before instantiation. instanceCounter = "
                + External.NestedFoo.getInstanceCounter());

        // Создадим экземпляр внешнего класса с псевдослучайными значениями полей:
        External instance = new External(2, 2);
        // Вызовем у объекта НЕстатический метод с уровнем доступа default:
        System.out.println("After first instance creation: " + instance.sum());

        // Снова выведем на экран число инстансов внешнего класса:
        External.messenger("After first instantiation. instanceCounter = "
                + External.NestedFoo.getInstanceCounter());

        // External.NestedFoo.methodFoExperiments(); // Ошибка! Ответьте себе почему?!

        External.NestedFoo nestedInstance = new External.NestedFoo();
        // Вот теперь можно получить доступ к нестатическому члену статического класса:
        nestedInstance.methodFoExperiments();
    }
}

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

Соответственно, для обращения к статическим членам вложенного статического класса нужно использовать его имя, а для обращения к НЕстатическим членам вложенного статического класса — нужно использовать имя его экземпляра. При этом, модификаторы доступа не влияют на видимость членов Nested(static) класса, для внешнего класса (private, default (package visible), protected, public).

Код-пример:

public class External {

    public static void main(String[] args) {
        NestedFoo first = new NestedFoo(); // Две равнозначные строки.
        External.NestedFoo second = new External.NestedFoo(); // Только имена ссылок разные.

        first.name = "first";
        first.number = 42;

        second.name = "second";
        second.number = Double.POSITIVE_INFINITY;

        first.internalNonStatic();
        second.internalNonStatic();

        NestedFoo.testInstance = null;
        NestedFoo.internalStatic();
    }

    static class NestedFoo {
        private String name;
        double number;
        static External testInstance = new External();

        private void internalNonStatic() {
            System.out.printf("Internal non-static method. Instance name: %s, instance number: %f\n", name, number);
        }

        private static void internalStatic() {
            System.out.println("Internal static method.\n");
        }
    }
}

Доступ к вложенному классу и его членам, из других классов, ограничивается модификаторами доступа и уровня.


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»