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

Конструкторы и this

В прошлом уроке точку собирали в три строки: new Point(), потом p.x = 3; и p.y = 4;. Если забыть одну из них, в поле останется 0, и компилятор промолчит. А десять точек потребуют тридцати строк.

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

JAVA
public class Main {
    public static void main(String[] args) {
        Point a = new Point(3, 4);
        System.out.println(a.x + " " + a.y); // 3 4
    }
}

class Point {
    int x;
    int y;
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

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

  1. Point(int x, int y) внутри класса — конструктор. Его имя совпадает с именем класса.
  2. new Point(3, 4) строит объект и сразу вызывает конструктор с аргументами 3 и 4.
  3. this.x = x; и this.y = y; кладут эти числа в поля нового объекта.
  4. Ссылка на готовую точку попадает в a, и println печатает 3 4.

Что такое конструктор

Конструктор похож на метод, но у него свои правила:

  • имя совпадает с именем класса, вплоть до заглавной буквы: Point;
  • типа результата нет, даже void: с void это был бы обычный метод по имени Point;
  • его вызывает new, ровно один раз для каждого объекта;
  • параметры работают как у метода: аргументы из new Point(3, 4) попадают в них по порядку.

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

this.x и x

Внутри конструктора есть два разных x. Первый — параметр: он появляется при вызове и исчезает, когда конструктор закончится. Второй — поле объекта, которое останется с точкой. Имя без уточнения означает ближайшую переменную, то есть параметр: он закрывает собой поле.

Чтобы добраться до поля, пишут this.x. this — ссылка на объект, который сейчас строится. Каждый new строит свой объект, поэтому и this у каждого вызова свой. Получается так:

  • this.x — поле x нового объекта;
  • x — параметр, то есть число из вызова new Point(3, 4).

В виджете программа строит две точки. Рамка Point(…) — это работающий конструктор: в ней видны параметры x и y и поля нового объекта this.x и this.y. Нажимайте «Следующий шаг» и следите, куда попадает каждое число.

Вживуюthis.x и x в конструкторешаг 1 из 11 · рамок: 1

new Point(3, 4) строит объект с полями x и y, равными 0, и вызывает конструктор.

Следите за рамкой Point(…): где параметр x, а где поле this.x

Когда конструктор заканчивается, this и параметры исчезают вместе с его рамкой, а объект остаётся, и ссылка на него попадает в a или b. Строка this.x = x во втором вызове не трогает первую точку: у второго вызова свой this.

Здесь картинка с бригадой неточна. Настоящая бригада начинает с пустого участка, а конструктор получает уже построенный объект. JVM создаёт его с полями по умолчанию, 0 у x и y, и только потом запускает конструктор.

Конструктор по умолчанию

Как же тогда работал new Point() в классе, где конструктора не было вовсе? Если в классе нет ни одного конструктора, Java сама добавляет пустой, без параметров. Это конструктор по умолчанию. Но стоит написать свой, и Java его больше не добавляет:

JAVA
Point a = new Point(3, 4);
Point b = new Point(); // ✗
TEXT
error: constructor Point in class Point cannot be applied to given types;

Программа не компилируется. В классе остался один конструктор, и он ждёт два int. Java не подставляет недостающие аргументы и не ищет пустой конструктор. Вызов должен подойти к одному из написанных конструкторов по числу и типам аргументов. Это проверяет компилятор, ещё до запуска.

Этот вопрос любят задавать на собеседовании: когда у класса есть конструктор по умолчанию? Только когда вы не написали ни одного своего.

Вызов this(…)

Чтобы new Point() снова работал, конструктор без параметров пишут сами. Конструкторов в классе может быть несколько, как у перегруженного метода, — лишь бы различались их параметры:

JAVA
class Point {
    int x;
    int y;
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    Point() {
        this(0, 0);
    }
}

Запись this(0, 0) вызывает другой конструктор того же класса — тот, что принимает два int. Поля заполняет один конструктор, а остальные передают работу ему. Если позже в основной конструктор добавится проверка, она сработает при любом способе создать точку.

Вызов this(…) пишут первой строкой конструктора. Java 21 другого порядка не допускает, а Java 25 разрешает перед ним некоторые инструкции, например проверку аргументов. В курсе код пишется так, чтобы он компилировался и на Java 21.

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

Забыли this перед одним из полей:

JAVA
class Point {
    int x;
    int y;
    Point(int x, int y) {
        this.x = x;
        y = y; // ✗
    }
}

Программа компилируется, а new Point(3, 4) даёт точку 3 0. Строка y = y кладёт параметр y в него же, а поле y так и остаётся 0. Компилятор об этом не предупреждает. Пишите this. перед каждым полем, у которого есть параметр с тем же именем: this.y = y;.

Главное

  • Конструктор называется как класс и не имеет типа результата; new вызывает его для каждого нового объекта.
  • Внутри конструктора this.x — поле нового объекта, а x — параметр; y = y без this поле не меняет.
  • Java добавляет конструктор по умолчанию, только если в классе нет ни одного своего конструктора.
  • this(…) первой строкой конструктора вызывает другой конструктор того же класса.