Ключевое слово static в Java: поля, методы, блоки и вложенные классы

Ключевое слово static в Java: поля, методы, блоки и вложенные классы Полезное

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

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

Что можно пометить как static

Модификатор применяется к четырем видам членов класса и участвует в импорте.

Элемент Что дает static Пример обращения
Поле одно значение на весь класс, общее для всех объектов User.count
Метод вызывается без объекта, внутри нет доступа к this Math.max(3, 7)
Блок инициализации выполняется один раз при инициализации класса тело static { ... }
Вложенный класс не хранит скрытую ссылку на внешний объект Outer.Node
import static доступ к статическому члену по короткому имени max(3, 7)

Дальше пройдем по каждому пункту с рабочим кодом.

Статические поля: одно значение на весь класс

Обычное поле (поле экземпляра) свое у каждого объекта. Статическое поле одно на весь класс: все объекты видят и меняют одно значение. Типичное применение — общий счетчик или константа.

public class User {
    static int count = 0;   // общий счетчик на весь класс
    int id;                 // свой у каждого объекта

    User() {
        count++;            // меняем общее значение
        id = count;         // фиксируем номер конкретного объекта
    }

    public static void main(String[] args) {
        User a = new User();
        User b = new User();
        System.out.println(a.id + " " + b.id);   // 1 2
        System.out.println(User.count);          // 2
    }
}

Поле count принадлежит не a или b, а классу User, поэтому к нему обращаются как User.count. Поле id у каждого объекта свое. Константы обычно делают static final.

Статические методы: работа без this

Статический метод вызывается у класса, без объекта. Отсюда его главное свойство: внутри нет неявной ссылки this, потому что нет объекта, который метод обслуживает. Примеры — Math.max, Integer.parseInt, а также main: JVM запускает программу до создания объектов, поэтому точка входа обязана быть статической.

public class MathUtils {
    static int square(int x) {   // не нужен объект
        return x * x;
    }

    public static void main(String[] args) {
        System.out.println(square(6));   // 36
    }
}

Сравнение двух видов методов удобно держать таблицей.

Признак Метод экземпляра Статический метод
Нужен объект для вызова да нет, вызывается у класса
Доступен this/super да нет
Доступ к полям экземпляра напрямую да нет, только через объект
Доступ к статическим полям да да
Участвует в переопределении (override) да нет, только скрытие (hiding)

Последняя строка — частая ловушка: статические методы не переопределяются полиморфно (подробнее в FAQ).

Типичная ошибка: обращение к нестатике из static

Самая частая ошибка новичка — попытка использовать нестатическое поле или метод из статического контекста, например из main. Компилятор такой код не пропустит.

public class Counter {
    int value = 5;          // поле экземпляра (нестатическое)

    public static void main(String[] args) {
        System.out.println(value);   // пытаемся прочитать поле объекта
    }
}

Фактический результат — ошибка компиляции (текст javac):

Counter.java:5: error: non-static variable value cannot be referenced from a static context
        System.out.println(value);   // пытаемся прочитать поле объекта
                           ^
1 error

Причина: value принадлежит объекту, а main статический и объекта не имеет — непонятно, у какого экземпляра брать значение. Исправить можно двумя способами.

Вариант 1 — если значение по смыслу общее для класса, сделать поле статическим:

public class Counter {
    static int value = 5;   // теперь поле принадлежит классу

    public static void main(String[] args) {
        System.out.println(value);   // выведет 5
    }
}

Вариант 2 — если значение свое у каждого объекта, сначала создать объект и обращаться через него: Counter c = new Counter(); System.out.println(c.value);.

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

Статические блоки инициализации

Статический блок static { ... } выполняется один раз при инициализации класса (перед первым использованием), до первого создания объекта и до вызова main. Он нужен, когда статическое поле нельзя инициализировать одним выражением.

public class Config {
    static final String url;

    static {
        String host = System.getenv("DB_HOST");
        url = (host != null ? host : "localhost") + "/app";
        System.out.println("Блок static выполнен один раз");
    }

    public static void main(String[] args) {
        System.out.println(url);   // напр. localhost/app
    }
}

Сначала в консоль попадет строка из блока, затем значение url. Сколько бы объектов Config ни создали, блок отработает один раз.

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

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

public class Outer {
    static class Node {   // не привязан к объекту Outer
        int data;
        Node next;
    }

    public static void main(String[] args) {
        Outer.Node n = new Outer.Node();   // без объекта Outer
        n.data = 42;
        System.out.println(n.data);        // 42
    }
}

Если убрать static у Node, создать узел как new Outer.Node() уже не выйдет — понадобится сначала объект Outer. Для структур данных, не зависящих от внешнего объекта, статический вариант и проще, и экономнее.

static import

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

import static java.lang.Math.max;
import static java.lang.Math.PI;

public class Demo {
    public static void main(String[] args) {
        System.out.println(max(3, 7));   // 7, без Math.
        System.out.println(PI);          // 3.141592653589793
    }
}

Прием полезен в тестах (assertEquals) и при частых обращениях к Math. Злоупотреблять не стоит: без префикса класса сложнее понять, откуда взялся вызов.

Когда использовать static, а когда нет

Короткое правило: static уместен там, где значение или поведение принадлежит классу, а не конкретному объекту.

  • Константы: static final для значений, единых для всех объектов.
  • Утилитные методы без состояния: расчеты и преобразования (как Math.max).
  • Общий счетчик или кэш на класс — но с оговоркой про потоки ниже.
  • Точка входа main — обязана быть статической.

Осторожно со статическим изменяемым состоянием: такое поле одно на класс, и его видят все потоки сразу. Если потоки читают и пишут его одновременно, нужна синхронизация (synchronized, java.util.concurrent.atomic). А static final со ссылкой на неизменяемый объект безопасно читать из любого потока. Избыточный static затрудняет тестирование, поэтому логику предметной области держат в обычных объектах.

Выводы

  • static привязывает член к классу, а не к объекту: одно значение или поведение на весь класс, доступ через имя класса без создания объекта.
  • Внутри статического метода нет this, поэтому напрямую обратиться к нестатическому полю нельзя — это ошибка компиляции; лечится либо переводом поля в static, либо созданием объекта.
  • Статический блок выполняется один раз при инициализации класса и служит для сложной инициализации статических полей.
  • Статический вложенный класс не держит ссылку на внешний объект и создается самостоятельно; static import дает короткие имена статических членов.
  • Статическое изменяемое поле — общее для всех потоков и требует синхронизации; static final на неизменяемый объект безопасен.

Где применяется / связь с практикой

static встречается в Java повсеместно: константы, утилитные классы, паттерн Singleton, фабричные методы, точка входа main. Понимание разницы между членом класса и объекта — базовый навык, без которого трудно читать чужой код.

Освойте тему на практике

Разобрать это на практике, с проверкой кода наставником, помогает курс Java Basic в Otus: классы, объекты и модификаторы доступа отрабатываются на заданиях с обратной связью. Посмотреть формат занятий можно на открытых уроках Otus — они бесплатные и проходят вживую.

FAQ

Можно ли обратиться к статическому полю через объект, например obj.count?
Технически да, компилятор трактует это как Класс.count. Но так делать не стоит: запись вводит в заблуждение, будто значение принадлежит объекту. Обращайтесь через имя класса.

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

Правда ли, что статические методы нельзя переопределить?
Полиморфного переопределения (override) у них нет: связывание идет по типу на этапе компиляции. Одноименный статический метод в подклассе не переопределяет, а скрывает метод родителя (method hiding).

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)