Перейти к содержанию

Руководство по ключевому слову static в Java

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

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

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

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

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

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

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

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

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

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

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

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

Однако предположим, что нужна переменная, которая содержит количество инициализированных объектов 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++;
    }

    // геттеры и сеттеры
}

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

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

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

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

  • когда значение переменной не зависит от объектов;
  • когда значение должно быть общим для всех объектов.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    static {
        ranks.add("Лейтенант");
        ranks.add("Капитан");
        ranks.add("Майор");
    }
    
    static {
        ranks.add("Полковник");
        ranks.add("Генерал");
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

public class Singleton  {
    private Singleton() {}

    private static class SingletonHolder {
        public static final Singleton instance = new Singleton();
    }

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

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

Веские причины использовать статический внутренний класс

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

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

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

Заключение

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

Полный код можно найти на GitHub.

Оригинал