W3docs

Ключевое слово static в Java

Объявляйте члены уровня класса в Java с помощью static — статические поля, методы, блоки и вложенные классы.

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

Единственное правило: у static-членов нет this. Всё остальное в их поведении вытекает из этого.

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

Статическое поле объявляется с static в теле класса. Существует ровно одна копия, независимо от количества экземпляров:

public class Counter {
  static int total;       // one Counter.total for everybody
  int count;              // each Counter has its own count
}

Counter a = new Counter();   a.count++;   Counter.total++;
Counter b = new Counter();   b.count++;   Counter.total++;

System.out.println(a.count);          // 1
System.out.println(b.count);          // 1
System.out.println(Counter.total);    // 2

Обращайтесь к статическому полю через имя класса (Counter.total) — это каноническая форма. Java также позволяет писать a.total, поскольку a является Counter, но любой линтер пометит такой стиль как нежелательный.

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

Статический метод принадлежит классу. Вызывайте его как ClassName.method(...):

public class MathUtil {
  public static int square(int n) {
    return n * n;
  }
}

int x = MathUtil.square(7);     // 49

Внутри статического метода нет this — нельзя обращаться к полям экземпляра или вызывать методы экземпляра без квалификатора, потому что нет текущего объекта, из которого их можно было бы прочитать:

public class Counter {
  int count;
  static void reset() {
    count = 0;      // ERROR: cannot reference instance field from static context
  }
}

Вы по-прежнему можете принять экземпляр в качестве параметра и работать с ним:

static void resetThis(Counter c) {
  c.count = 0;     // ok — c is a parameter, not this
}

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

Когда использовать static

Единственный критерий — зависит ли член от состояния конкретного экземпляра.

Зависит от this?Использовать
Нет — чистое вычисление, общий счётчик, фабричный методstatic
Да — читает или изменяет собственные поля объектаэкземплярный
public static int hoursToSeconds(int h) { return h * 3600; }   // no this needed → static
public        int rentalDays()           { return days; }       // reads this.days → instance

Распространённый запах кода: метод экземпляра, который игнорирует this. Он лжёт о том, что является специфичным для экземпляра — сделайте его static, и вызывающему коду больше не придётся создавать объект только для его использования.

Статические константы

Сочетание static final — стандартная форма для констант:

public static final double TAX_RATE = 0.08;
public static final int    MAX_RETRIES = 3;
public static final String DEFAULT_LOCALE = "en";

По соглашению константы именуются в UPPER_SNAKE_CASE. Они встраиваются во время компиляции, когда значение является примитивом или строковым литералом (String), что делает их такими же быстрыми, как литерал в месте вызова.

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

Блок static { ... } выполняется один раз при первой загрузке класса в JVM. Используйте его для однократной инициализации статических полей, которым требуется больше одного выражения:

public class Lookup {
  static final Map<String, Integer> WEEKDAYS = new HashMap<>();
  static {
    WEEKDAYS.put("Mon", 1);
    WEEKDAYS.put("Tue", 2);
    WEEKDAYS.put("Wed", 3);
    WEEKDAYS.put("Thu", 4);
    WEEKDAYS.put("Fri", 5);
    WEEKDAYS.put("Sat", 6);
    WEEKDAYS.put("Sun", 7);
  }
}

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

Статические поля и статические блоки выполняются сверху вниз в том порядке, в котором они появляются в исходном коде. Если инициализатор обращается к полю (через метод) до того, как выполнилась строка с объявлением этого поля, он видит значение по умолчанию (0, false или null) — поэтому порядок имеет значение:

public class Order {
  static int a = compute();          // runs first; b is still 0 → a becomes 1
  static int b = 10;                 // runs second
  static int compute() { return b + 1; }
}
// Order.a == 1, Order.b == 10

(Обращение к b напрямую в той первой строке — static int a = b + 1; — является вместо этого ошибкой компиляции «illegal forward reference»; лишь косвенный путь через метод открывает доступ к значению по умолчанию.)

Статические вложенные классы

Класс, объявленный static внутри другого класса, называется статическим вложенным классом. В отличие от нестатического внутреннего класса, он не несёт неявной ссылки на экземпляр внешнего класса:

public class Outer {
  static class Inner {           // does not need an Outer instance
    void hello() { System.out.println("hi"); }
  }
}

Outer.Inner i = new Outer.Inner();   // create directly
i.hello();

Именно так мы оборачивали вспомогательные классы в том же файле, что и main, в более ранних блоках static class Foo {...}main является статическим, поэтому вспомогательный класс тоже должен быть статическим, чтобы создавать его экземпляры без охватывающего объекта Outer. Глава о вложенных классах охватывает все четыре варианта.

static — это не «конструктор»

Новички иногда путают «static = выполняется один раз» с «static = выполняется при создании объекта». Статическая инициализация происходит один раз на класс, а не один раз на объект. Конструктор выполняется один раз на объект. Это отдельные механизмы, срабатывающие в разных ситуациях.

Статические члены в интерфейсах

Интерфейсы тоже могут иметь static-методы. Они ведут себя так же, как статические члены классов — вызывайте их как InterfaceName.method(...):

public interface Path {
  static Path of(String s) { return new SimplePath(s); }
}

Path p = Path.of("/tmp/foo");

Глава об интерфейсах рассматривает, когда это уместно (фабричные методы непосредственно на интерфейсе).

Практический пример

java— editable, runs on the server

Программа выводит:

#1  apple   base=0.50 USD,  with tax=0.54
#2  bread   base=2.40 USD,  with tax=2.59
#3  butter  base=3.10 USD,  with tax=3.35
Total items ever created: 3

Обратите внимание, как пример сочетает все четыре варианта: константы и таблица подстановки заполняются до создания какого-либо Item, каждый Item хранит собственные name и basePrice, а единственное общее поле itemsCreated раздаёт порядковый id.

Что дальше

static позволяет указать кому принадлежит член. Следующий инструмент — final, который позволяет указать можно ли его изменить. Вместе static final — стандартная форма для констант; само по себе final определяет наследование и неизменяемый дизайн. Продолжайте с java-final.

Практика

Практика
Почему статический метод получает ошибку компиляции, если пытается прочитать поле экземпляра по его простому имени?
Почему статический метод получает ошибку компиляции, если пытается прочитать поле экземпляра по его простому имени?
Was this page helpful?