«1. Обзор

В этом руководстве мы подробно рассмотрим ключевое слово static языка Java.

Мы узнаем, как мы можем применять ключевое слово static к переменным, методам, блокам и вложенным классам, и в чем разница.

2. Анатомия ключевого слова static

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

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

Мы можем применить ключевое слово к переменным, методам, блокам и вложенным классам.

3. Статические поля (или переменные класса)

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

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

С точки зрения памяти статические переменные хранятся в куче памяти.

3.1. Пример статического поля

Допустим, у нас есть класс Car с несколькими атрибутами (переменными экземпляра).

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

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

Здесь на помощь приходят статические переменные:

public class Car {
    private String name;
    private String engine;
    
    public static int numberOfCars;
    
    public Car(String name, String engine) {
        this.name = name;
        this.engine = engine;
        numberOfCars++;
    }

    // getters and setters
}

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

Итак, для этого случая будут верны:

@Test
public void whenNumberOfCarObjectsInitialized_thenStaticCounterIncreases() {
    new Car("Jaguar", "V8");
    new Car("Bugatti", "W16");
 
    assertEquals(2, Car.numberOfCars);
}

3.2. Веские причины для использования статических полей

    Когда значение переменной не зависит от объектов Когда предполагается, что значение является общим для всех объектов

3.3. Ключевые моменты, которые следует помнить

    Поскольку статические переменные принадлежат классу, мы можем обращаться к ним напрямую, используя имя класса. Итак, нам не нужна ссылка на объект. Мы можем объявлять статические переменные только на уровне класса. Мы можем получить доступ к статическим полям без инициализации объекта. Наконец, мы можем получить доступ к статическим полям, используя ссылку на объект (например, ford.numberOfCars++). Но мы должны избегать этого, потому что становится трудно понять, является ли это переменной экземпляра или переменной класса. Вместо этого мы всегда должны обращаться к статическим переменным, используя имя класса (Car.numberOfCars++).

4. Статические методы (или методы класса)

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

4.1. Пример статического метода

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

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

public static void setNumberOfCars(int numberOfCars) {
    Car.numberOfCars = numberOfCars;
}

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

Просто взгляните на служебные классы Collections или Math из JDK, StringUtils из Apache или CollectionUtils из Spring framework и обратите внимание, что все методы являются статическими.

4.2. Веские причины использовать статические методы

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

4.3. Ключевые моменты, которые следует помнить

    «статические методы в Java разрешаются во время компиляции. Поскольку переопределение методов является частью полиморфизма времени выполнения, статические методы нельзя переопределять. Абстрактные методы не могут быть статическими. статические методы не могут использовать ключевые слова this или super. Допустимы следующие комбинации экземпляра, методов класса и переменных: Методы экземпляра могут напрямую обращаться как к методам экземпляра, так и к переменным экземпляра. Методы экземпляра также могут напрямую обращаться к статическим переменным и статическим методам. статические методы могут обращаться ко всем статическим переменным и другим статическим методам. статические методы не могут напрямую обращаться к переменным экземпляра и методам экземпляра. Для этого им нужна некоторая ссылка на объект.

5. Статический блок

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

В таких случаях на помощь приходят статические блоки.

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

5.1. Пример статического блока

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

Это упрощается со статическими блоками:

public class StaticBlockDemo {
    public static List<String> ranks = new LinkedList<>();

    static {
        ranks.add("Lieutenant");
        ranks.add("Captain");
        ranks.add("Major");
    }
    
    static {
        ranks.add("Colonel");
        ranks.add("General");
    }
}

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

5.2. Веские причины использовать статические блоки

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

5.3. Ключевые моменты, которые следует помнить

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

6. Статический класс

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

Архитектура вложенных классов делится на две части:

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

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

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

6.1. Пример статического класса

Наиболее широко используемый подход к созданию одноэлементных объектов — через статический вложенный класс:

public class Singleton { private Singleton() {}

private static class SingletonHolder { public static final Singleton instance = новый синглтон(); }

public static Singleton getInstance() { return SingletonHolder.instance; } }

Мы используем этот метод, потому что он не требует никакой синхронизации и прост в освоении и реализации.

6.2. Веские причины для использования статического внутреннего класса

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

6.3. Ключевые моменты, которые следует помнить

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

7. Заключение

В этой статье мы увидели ключевое слово static в действии.

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

Как всегда, мы можем найти полный код на GitHub.