Перейти к содержимому
Теория · Классы и объекты · урок 7 из 10

static: общее для всех

Как вы уже знаете, метод со словом static принадлежит самому чертежу, а не дому. Поля тоже бывают такими. Представьте, что на чертеже есть строка «домов построено», и каждая бригада после стройки прибавляет к ней единицу. Строка одна на весь чертёж. Если бы она была своя в каждом доме, в каждом было бы записано «1».

Программе часто нужно такое общее значение: сколько объектов создано, какой номер выдать следующему. Посмотрим, как это сделать:

JAVA
public class Main {
    public static void main(String[] args) {
        House a = new House();
        House b = new House();
        System.out.println(House.count); // 2
    }
}

class House {
    static int count;
    int number;
    House() {
        number = ++count;
    }
}

Давайте разберём программу по шагам:

  1. static int count; — одно поле на весь класс House, и в нём сразу лежит 0.
  2. int number; — обычное поле: у каждого дома своё.
  3. Конструктор выполняет number = ++count;. Запись ++count сначала прибавляет к общему count единицу, а потом отдаёт новое значение, и оно попадает в number этого дома.
  4. Первый дом получает номер 1, второй — 2, а count после двух new равен 2.
  5. House.count читает поле через имя класса и печатает 2.

Номера у домов разные, а счётчик общий:

JAVA
System.out.println(a.number); // 1
System.out.println(b.number); // 2
new House();
System.out.println(House.count); // 3

Строка new House(); строит дом и никуда не сохраняет ссылку на него. Так можно: конструктор всё равно выполнится, и count вырастет до 3.

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

Статическое поле — поле со словом static: оно одно на весь класс, сколько бы объектов ни было. Сравним два вида полей:

Поле объектаСтатическое поле
Объявлениеint number;static int count;
Сколько копийпо одной в каждом объектеодна на весь класс
Как обратиться снаружичерез объект: a.numberчерез класс: House.count
Когда появляетсяпри каждом newкогда JVM загружает класс, до первого объекта

Внутри своего класса к статическому полю обращаются по одному имени, как к count в конструкторе. Поэтому поля и методы рядом с main до сих пор писались со static. Объект класса Main никто не создавал, и работать можно было только с тем, что принадлежит самому классу.

Здесь картинка с чертежом неточна. Строку на настоящем чертеже исправляет только бригада, а статическое поле без private может поменять любой код программы: House.count = 100; скомпилируется.

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

Статический метод тоже принадлежит классу, и его вызывают через имя класса: Math.addExact(2, 3) — статический метод класса Math. Объекта при таком вызове нет, а значит, нет и this. Поэтому статический метод не может прочитать поле объекта:

JAVA
class House {
    static int count;
    int number;
    static void show() {
        System.out.println(number); // ✗
    }
}
TEXT
error: non-static variable number cannot be referenced from a static context

Программа не компилируется. Вызов House.show() не говорит, о каком доме речь, поэтому неизвестно, чей номер печатать. А count метод show прочитать может: это поле одно на весь класс. С полем объекта статический метод работает, только если получит сам объект — параметром или через new.

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

Частая ошибка

Компилятор пишет non-static variable … cannot be referenced from a static context, и поле делают static, чтобы ошибка ушла. Ошибка уходит, а вместе с ней — смысл поля:

JAVA
public class Main {
    public static void main(String[] args) {
        House a = new House("red");
        House b = new House("blue");
        System.out.println(a.color); // blue
    }
}

class House {
    static String color; // ✗
    House(String color) {
        this.color = color;
    }
}

Программа печатает blue, хотя дом a строили красным. Поле color одно на весь класс, и второй вызов конструктора перезаписал его. Обычный javac молчит, и только javac -Xlint:all предупредит, что к статическому полю обращаются через объект. Цвет — свойство конкретного дома, поэтому static здесь не нужен: String color;.

Главное

  • Статическое поле одно на весь класс: его делят все объекты, а снаружи к нему обращаются через класс — House.count.
  • Поле без static своё у каждого объекта: a.number и b.number хранят разные числа.
  • У статического метода нет this, поэтому поле объекта он может прочитать только через ссылку на объект.
  • static у поля, которое должно быть своим у каждого объекта, делает его общим: все дома получат один цвет.