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

1. Введение

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

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

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

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

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

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

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

С точки зрения памяти статические переменные помещаются в определенный пул в памяти JVM, называемый Metaspace (до Java 8 этот пул назывался Permanent Generation или PermGen, который был полностью удален и заменен на Metaspace).

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

Предположим, у нас есть класс 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++; } // 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 и обратите внимание, что все методы являются статическими .

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

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

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

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

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

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

В таких случаях пригодятся статические блоки.

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

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

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

Это легко сделать со статическими блоками:

public class StaticBlockDemo { public static List 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 = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.instance; } }

6.2. Compelling Reasons to Use a static Inner Class

  • Grouping classes that will be used only in one place increases encapsulation
  • The code is brought closer to the place that will be only one to use it; this increases readability and code is more maintainable
  • If nested class doesn't require any access to it's enclosing class instance members, then it's better to declare it as static because this way, it won't be coupled to the outer class and hence will be more optimal as they won't require any heap or stack memory

6.3. Key Points to Remember

  • static nested classes do not have access to any instance members of the enclosing outer class; it can only access them through an object's reference
  • статические вложенные классы могут получить доступ ко всем статическим членам включающего класса, включая частные
  • Спецификация программирования Java не позволяет нам объявить класс верхнего уровня статическим ; только классы внутри классов (вложенные классы) могут быть статическими

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

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

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