Статические поля и методы

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

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

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

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

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

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

StaticTest.java
class StaticTest {
    static int val = 924;
}

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

StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();

В данном примере как st1.val, так и st2.val имеют одинаковые значения, равные 924, потому что они ссылаются на один блок памяти.

Существует два способа обратиться к статической переменной. Как было видно выше, на нее можно ссылаться по имени объекта, например st2.val. Также возможно обратиться к ней прямо через имя класса; для нестатических членов класса такая возможность отсутствует.

StaticTest.val++;

После выполнения операции, значения st1.val и st2.val будут равны 48.

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

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

class NetworkConnector {

    public static final int baud;
    private static final int bits_per_interval = 4;

    static {
        baud = 9600 / bits_per_interval;
    }

    // ...
}

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

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

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

class Incrementable {
    static void increment() {
        StaticTest.val++;
    }
}

Нетрудно заметить, что метод increment() класса Incrementable увеличивает значение статического поля val. Метод можно вызвать стандартно, через объект:

Incrementable sf = new Incrementable();
sf.increment();

Или, поскольку метод increment() является статическим, можно вызвать его с прямым указанием класса:

Incrementable.increment();

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

На статические методы налагаются следующие ограничения:

  • они могут непосредственно вызывать только другие статические методы;

  • им непосредственно доступны только статические переменные;

  • им недоступны ключевые слова this или super.

Статический импорт

В языке Java имеется языковое средство, расширяющее возможности ключевого слова import и называемое статическим импортом. Оператор import, предваряемый ключевым словом static, можно применять для импорта статических членов класса или интерфейса. Благодаря статическому импорту появляется возможность ссылаться на статические члены непосредственно по именам, не используя имя класса. Это упрощает и сокращает синтаксис, требующийся для работы со статическими членами.

Рассмотрим пример без использования статического импорта. Представим себе, что мы вычисляем гипотенузу прямоугольного треугольника. Мы будем часто использовать методы Math.pow() и Math.sqrt()

double side1 = 3.0;
double side2 = 4.0;
double hypot;

hypot = Math.sqrt(Math.pow(side1, 2) + Math.pow(si de2, 2));

Как мы видим, строка

hypot = Math.sqrt(Math.pow(side1, 2) + Math.pow(side2, 2));

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

import static java.lang.Math.pow;
import static java.lang.Math.sqrt;

public class Main {
    public static void main(String[] args) {

        double side1 = 3.0;
        double side2 = 4.0;
        double hypot;

        hypot = sqrt(pow(side1, 2) + pow(side2, 2));
    }
}

После использования статического импорта нет нужды использовать имя класс Math для вызова статических методов pow() и sqrt().

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

import static java.lang.Math.*;

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

Last updated