Класс и объект
Чему вы научитесь:
- описывать свой тип классом, создавать объекты через
newи объяснять, почему две переменные могут вести к одному объекту; - задавать поля объекта конструктором и различать внутри него
this.xиx; - закрывать поля словом
privateи отличать поле объекта отstatic-поля; - печатать объект через
toStringи читать сообщениеNullPointerException.
Представьте чертёж дома. На нём написано, что будет у каждого дома: сколько этажей, какого цвета крыша. Жить в чертеже нельзя, это только описание. Зато по одному чертежу можно построить целую улицу. Дома выйдут похожими, но у каждого своя крыша, и если перекрасить один дом, соседний останется прежним.
Java устроена так же. Чертёж — это класс: он описывает, какие данные будут у каждого объекта. Дом — это объект, который программа строит по чертежу, пока работает.
Зачем это нужно? Возьмём точку на плоскости. Это два числа, x и y, и они всегда идут вместе. Для сотни точек понадобилось бы двести переменных, и x одной точки оказался бы рядом с y другой. Класс позволяет сказать Java: «точка — это пара чисел», и дальше работать с точкой как с одной вещью:
public class Main {
public static void main(String[] args) {
Point p = new Point();
p.x = 3;
p.y = 4;
System.out.println(p.x + " " + p.y); // 3 4
Point q = new Point();
System.out.println(q.x + " " + q.y); // 0 0
}
}
class Point {
int x;
int y;
}Давайте разберём программу по шагам:
- Внизу файла объявлен класс
Pointс двумя полями:xиy. Это чертёж. new Point()строит по чертежу объект — одну точку, аPoint pзаводит для неё переменную.p.x = 3;иp.y = 4;кладут числа в поля этой точки. Знак.между именем переменной и именем поля читается как «полеxтой точки, к которой ведётp».- Первый
printlnпечатает3 4. new Point()строит вторую точку. В её поля никто ничего не клал, поэтому печатается0 0.
Что описывает класс
Класс — описание нового типа: какие поля будут у каждого его объекта. После объявления class Point в программе есть тип Point, и переменную этого типа заводят так же, как переменную int или String.
Объект — одна конкретная вещь, построенная по классу словом new. Объект ещё называют экземпляром класса. Что важно знать об объектах:
- у каждого объекта свои поля: запись в
p.xне меняетq.x; - поля нового объекта получают значения по умолчанию, как поля
static:0у чисел,falseуboolean,nullуString; - объектов одного класса можно создать сколько угодно, и каждый
newстроит новый.
Класс Point объявлен в том же файле, после Main, и без слова public. В одном файле может быть только один public-класс, и файл называется по его имени — Main.java.
Переменная хранит адрес
Вернёмся к улице. Переменная p хранит не сам дом, а бумажку с его адресом. Как и с массивом, это ссылка: new Point() строит объект в куче и возвращает ссылку на него. Посмотрим, что получится, если скопировать такую бумажку:
Point a = new Point();
Point b = a;
b.x = 7;
System.out.println(a.x); // 7Разберём по строкам:
Point a = new Point();строит дом и кладёт его адрес вa.Point b = a;копирует бумажку с адресом, а не дом. Теперьaиbведут к одному объекту.b.x = 7;идёт по адресу изbи меняет поле этого объекта.a.xпечатает7: по адресу изaстоит тот же дом.
Если теперь написать b = new Point();, на бумажке b окажется адрес нового дома. Старый дом от этого не пострадает: a по-прежнему ведёт к нему, и a.x останется 7.
Здесь картинка с домами неточна. В настоящий дом можно прийти и без бумажки, если знать улицу. А к объекту программа попадает только по ссылке: если ни одна переменная не ведёт к объекту, работать с ним уже нельзя.
Объект в параметре метода
Этот вопрос часто задают на собеседовании: может ли метод изменить объект, который ему передали? Давайте посмотрим:
public class Main {
static void move(Point p) {
p.x = 10;
p = new Point();
p.x = 20;
}
public static void main(String[] args) {
Point a = new Point();
move(a);
System.out.println(a.x); // 10
}
}
class Point { int x; int y; }Класс Point здесь тот же, только записан в одну строку. Разберём вызов move(a) по шагам:
- Параметр
pполучает копию значенияa, то есть копию адреса. Теперьaиpведут к одной точке. p.x = 10;меняет поле этой точки, и изменение увидитmain.p = new Point();пишет на бумажкуpадрес новой точки. Бумажкаaвmainосталась прежней.p.x = 20;меняет уже новую точку, о которойmainне знает.mainпечатает10.
Ответ на собеседовании звучит так: Java всегда передаёт аргументы по значению, а значение переменной с объектом — ссылка на него. Поэтому метод может изменить поля объекта, но не может заставить переменную вызывающего кода вести к другому объекту.
Частая ошибка
Объявили переменную, но забыли построить объект:
Point p;
p.x = 3; // ✗error: variable p might not have been initializedПрограмма не компилируется. Point p; заводит только бумажку для адреса, а дом никто не строил, и записать 3 некуда. Объект создаёт только new: Point p = new Point();.
Главное
- Класс описывает поля нового типа, а
newстроит по нему объект; у каждого объекта свои поля. - Переменная типа
Pointхранит ссылку на объект, поэтомуPoint b = aкопирует ссылку, иaиbведут к одному объекту. - Метод получает копию ссылки: он может изменить поля объекта, но присваивание параметру не меняет переменную вызывающего кода.
- Без
newобъекта нет:Point p;не создаёт точку.