static: общее для всех
Как вы уже знаете, метод со словом static принадлежит самому чертежу, а не дому. Поля тоже бывают такими. Представьте, что на чертеже есть строка «домов построено», и каждая бригада после стройки прибавляет к ней единицу. Строка одна на весь чертёж. Если бы она была своя в каждом доме, в каждом было бы записано «1».
Программе часто нужно такое общее значение: сколько объектов создано, какой номер выдать следующему. Посмотрим, как это сделать:
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;
}
}Давайте разберём программу по шагам:
static int count;— одно поле на весь классHouse, и в нём сразу лежит0.int number;— обычное поле: у каждого дома своё.- Конструктор выполняет
number = ++count;. Запись++countсначала прибавляет к общемуcountединицу, а потом отдаёт новое значение, и оно попадает вnumberэтого дома. - Первый дом получает номер
1, второй —2, аcountпосле двухnewравен2. House.countчитает поле через имя класса и печатает2.
Номера у домов разные, а счётчик общий:
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. Поэтому статический метод не может прочитать поле объекта:
class House {
static int count;
int number;
static void show() {
System.out.println(number); // ✗
}
}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, чтобы ошибка ушла. Ошибка уходит, а вместе с ней — смысл поля:
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у поля, которое должно быть своим у каждого объекта, делает его общим: все дома получат один цвет.