Ключевое слово 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");Глава об интерфейсах рассматривает, когда это уместно (фабричные методы непосредственно на интерфейсе).
Практический пример
Программа выводит:
#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.