Статические переменные в Java
Два разных объекта, две разные записи — staticVars1.b = 3; и staticVars2.b = 4;, — а на экран оба раза выводится 4. Это не ошибка: переменная b объявлена как static, и оба объекта на самом деле работают с одной и той же ячейкой памяти.
Статическая переменная (static-поле, переменная класса) в Java — это поле, объявленное внутри класса с ключевым словом static. Она принадлежит самому классу, а не его объектам: существует в единственном экземпляре на весь класс, создаётся один раз при загрузке и инициализации класса и доступна без создания объекта.
Что означает static в Java
Ключевое слово static используется для объявления статических членов класса — переменных и методов, а также статических блоков инициализации и статических вложенных классов. Разница между статическими и обычными членами класса в том, что к обычному члену можно обратиться только через объект его класса. Член класса, объявленный с static, доступен и без ссылки на какой-либо объект. Общий обзор всех вариантов использования ключевого слова — в уроке Ключевое слово static.
При создании объектов копии статических переменных не создаются: сколько бы объектов ни было, статическая переменная одна на весь класс. Её часто сравнивают с глобальной переменной, но это сравнение условное: в Java нет глобальных переменных в привычном смысле — static-поле всегда принадлежит конкретному классу, и его видимость ограничивается модификаторами доступа (private, public и т.д.).
Как и обычные поля, статические переменные получают значения по умолчанию: 0 для числовых типов, false для boolean, null для ссылочных типов.
Статические и обычные переменные: пример
Рассмотрим различие между обычными и статическими переменными на примере. Для обращения к обычной переменной a необходим объект класса StaticVars. К переменной b можно обращаться без упоминания объекта и даже без упоминания класса, если обращение происходит из того же класса:
public class StaticVars {
int a;
static int b;
public static void main(String[] args) {
StaticVars staticVars = new StaticVars();
System.out.println(staticVars.a);
System.out.println(b);
}
}
Как обращаться к статической переменной
При обращении к статической переменной из другого класса нужно указать имя её класса — StaticVars.b. Технически можно обратиться к ней и через любой объект того же класса, например staticVars1.b или staticVars2.b. Но так делать не рекомендуется: читатель кода может решить, что это обычная переменная объекта, а IDE выдаст предупреждение.
public class StaticVarsExample {
public static void main(String[] args) {
StaticVars staticVars1 = new StaticVars();
StaticVars staticVars2 = new StaticVars();
System.out.println(StaticVars.b);
System.out.println(staticVars1.b);
staticVars1.b = 3;
staticVars2.b = 4;
System.out.println(staticVars1.b);
System.out.println(staticVars2.b);
}
}
Результат выполнения кода:
0
0
4
4 Обе записи изменили одну и ту же переменную, поэтому последнее присвоенное значение 4 видно через любой объект.
Хорошая практика
Всегда обращайтесь к статической переменной через имя класса: StaticVars.b, а не staticVars1.b. Так сразу видно, что значение общее для всех объектов. Если класс часто использует чужие константы, их можно подключить через static import.
Сравнительная таблица: статическая и обычная переменная
| Характеристика | Обычная переменная (поле объекта) | Статическая переменная (поле класса) |
|---|---|---|
| Объявление | int a; | static int b; |
| Количество копий | Своя у каждого объекта | Одна на весь класс |
| Когда создаётся | При создании объекта (new) | Один раз при загрузке и инициализации класса |
| Как обращаться | Через объект: obj.a | Через имя класса: StaticVars.b |
| Доступ из static-метода | Только через ссылку на объект | Напрямую |
| Типичное применение | Состояние конкретного объекта (цвет, имя) | Счётчики, константы, общие настройки |
Пример: счётчик созданных объектов
Классический пример использования статической переменной — подсчёт количества созданных объектов класса Ball. Для этого объявим в классе статическую переменную count. При создании объекта всегда вызывается конструктор, поэтому именно в нём будем увеличивать count. Для доступа к private переменной count определён статический метод getCount():
public class Ball {
private static int count = 0;
String color = "none";
public Ball(String color) {
this.color = color;
count++;
}
public static int getCount() {
return count;
}
} public class BallExample {
public static void main(String[] args) {
Ball ball1 = new Ball("красный");
Ball ball2 = new Ball("голубой");
System.out.println("Количество созданных объектов: "
+ Ball.getCount());
}
} Результат выполнения кода:
Количество созданных объектов: 2 У каждого мяча свой color, но count общий: оба конструктора увеличили одну и ту же переменную.
Константы: static final
Самый частый на практике вид статических переменных — константы. Их объявляют с двумя модификаторами сразу: static (одно значение на класс) и final (значение нельзя изменить после присваивания). По соглашению имена констант пишут в верхнем регистре через подчёркивание:
public class AppConfig {
public static final int MAX_USERS = 100;
public static final String APP_NAME = "ExamClouds";
public static final double TAX_RATE = 0.2;
} public class AppConfigExample {
public static void main(String[] args) {
System.out.println(AppConfig.APP_NAME + ": " + AppConfig.MAX_USERS);
// AppConfig.MAX_USERS = 200; // ошибка компиляции: final-переменную нельзя изменить
}
} Если константа примитивного типа или String инициализируется выражением, известным на этапе компиляции, компилятор подставляет её значение прямо в места использования (compile-time constant). Такие константы можно использовать, например, в метках case оператора switch.
Нюанс
final у ссылочной переменной запрещает менять саму ссылку, но не содержимое объекта. Константа static final List<String> NAMES = new ArrayList<>(); по-прежнему позволяет вызвать NAMES.add(...). Для по-настоящему неизменяемой коллекции используйте List.of(...).
Частые ошибки со статическими переменными
- static у локальной переменной. Внутри метода объявить
static int x = 0;нельзя — это ошибка компиляции. Модификаторstaticдопустим только для полей класса. - Обращение к обычному полю из static-метода. В
main()или любом другом статическом методе нельзя написать простоa, еслиa— нестатическое поле: компилятор сообщит non-static variable cannot be referenced from a static context. Нужен объект:new StaticVars().a. - Изменяемое общее состояние. Статическая переменная видна из всех объектов и потоков. Операция
count++не атомарна, поэтому при создании объектовBallиз нескольких потоков счётчик может «потерять» увеличения. В многопоточном коде используйтеAtomicIntegerили синхронизацию. - Злоупотребление static. Если значение по смыслу относится к конкретному объекту (цвет мяча, имя пользователя), оно не должно быть статическим — иначе все объекты будут перезаписывать одно поле.
Закрепить материал можно на практических задачах по static.
Часто задаваемые вопросы
Где хранятся статические переменные в памяти?
Начиная с JDK 7 (HotSpot) статические поля хранятся в куче (heap) вместе с объектом Class соответствующего класса; до этого они находились в PermGen. В Java 8 PermGen удалена и заменена на Metaspace, где хранятся метаданные классов, но не значения static-полей.
Можно ли обратиться к статической переменной через null-ссылку?
Да, и это популярный вопрос на собеседованиях. Код StaticVars s = null; System.out.println(s.b); не выбросит NullPointerException: компилятор заменяет обращение через переменную на обращение через тип, StaticVars.b, и объект вообще не используется.
Есть ли в Java ключевое слово const?
const в Java зарезервировано, но не используется — объявить с ним переменную нельзя. Константы в Java создаются сочетанием модификаторов static final, например public static final int MAX_USERS = 100;.
Чем static отличается от final?
static определяет, кому принадлежит переменная: классу (одна копия) или объекту (своя копия у каждого). final определяет, можно ли её изменить после присваивания. Модификаторы независимы: бывают static-переменные без final (счётчик), final-поля без static (неизменяемый id объекта) и их сочетание — константы.
Когда инициализируется статическая переменная?
Один раз при инициализации класса — перед первым созданием объекта, первым вызовом статического метода или первым обращением к статическому полю (кроме compile-time констант). Инициализаторы static-полей и статические блоки выполняются в порядке их объявления в классе.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии