Class и object в Java: классы и объекты для новичка

Class и object в Java: классы и объекты для новичка Полезное

Класс в Java — это описание (чертеж), по которому создаются объекты: он задает, какие у будущего объекта будут поля (данные) и методы (поведение). Объект — это конкретный экземпляр класса, созданный по этому чертежу и живущий в памяти. Одно объявление класса — и сколько угодно объектов по нему. Ниже разберем, чем класс отличается от объекта, что такое поля и методы, зачем нужен конструктор, ключевое слово new, this и ссылки. Все примеры запускаются на Java 25 LTS.

Класс и объект: чертеж против конкретной вещи

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

Путаница «класс или объект» — самая частая у новичка, поэтому развожу термины сразу и явно.

Признак Класс (class) Объект (object)
Что это описание, чертеж конкретный экземпляр
Сколько бывает один на тип сколько угодно
Память под данные само объявление не выделяет память под поля экземпляра под свои поля выделяется при создании
Как появляется пишется в коде через class создается через new
Пример Cat barsik, murka

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

Минимальный рабочий пример целиком

Сначала — законченный класс, который компилируется и печатает результат. Разбор по строкам идет ниже.

public class Cat {
    String name;   // поле: кличка
    int age;       // поле: возраст

    void meow() {  // метод: поведение
        System.out.println(name + " говорит: мяу");
    }

    public static void main(String[] args) {
        Cat cat = new Cat();   // создаем объект по чертежу Cat
        cat.name = "Барсик";   // задаем поля объекта
        cat.age = 3;
        cat.meow();            // вызываем метод объекта
        System.out.println(cat.name + ", возраст " + cat.age);
    }
}

Вывод в консоли:

Барсик говорит: мяу
Барсик, возраст 3

Что здесь произошло по шагам:

  • class Cat { ... } — объявление класса. Пока это только чертеж, ни одного кота еще нет.
  • String name; и int age; — поля (переменные экземпляра). Это состояние будущего объекта.
  • void meow() — метод. Это поведение объекта, именованный блок кода, который можно вызывать много раз.
  • new Cat() — создание объекта. Именно здесь под кота выделяется память, и переменная cat получает ссылку на него.
  • cat.name, cat.meow() — обращение к полям и методам конкретного объекта через точку.

Поля и методы: данные и поведение

Поле — это переменная, объявленная внутри класса и хранящая часть состояния объекта. У кота это кличка и возраст. Метод — это действие, которое объект умеет выполнять: у кота это meow().

Метод может принимать аргументы и возвращать значение. Тип возвращаемого значения пишется перед именем метода, а void означает, что метод ничего не возвращает.

int max(int a, int b) {   // принимает два числа, возвращает большее
    if (a > b) {
        return a;
    }
    return b;
}

Если вызвать max(7, 4), метод вернет 7. Обратите внимание: у метода max в скобках объявлены параметры a и b, а конкретные 7 и 4 при вызове — это аргументы.

Конструктор и ключевое слово this

Задавать поля по одному после создания (cat.name = ...) неудобно и легко забыть. Для этого есть конструктор — специальный метод, который вызывается при new и заполняет объект начальными данными. Имя конструктора совпадает с именем класса, а тип возвращаемого значения у него не указывается вовсе.

public class Cat {
    String name;
    int age;

    Cat(String name, int age) {   // конструктор
        this.name = name;         // this.name - поле объекта
        this.age = age;           // name - параметр конструктора
    }

    void meow() {
        System.out.println(name + " говорит: мяу");
    }

    public static void main(String[] args) {
        Cat barsik = new Cat("Барсик", 3);
        Cat murka = new Cat("Мурка", 5);
        barsik.meow();
        System.out.println(murka.name + ", возраст " + murka.age);
    }
}

Вывод:

Барсик говорит: мяу
Мурка, возраст 5

Здесь важен this. Параметр конструктора называется name, и поле объекта тоже называется name. Внутри конструктора простое name — это параметр, а this.name — поле текущего объекта. this — это ссылка на тот объект, для которого сейчас работает метод или конструктор. Строка this.name = name читается как «положить значение параметра в поле объекта». Если убрать this, поле не заполнится: присваивание name = name просто перезапишет параметр сам в себя.

Обратите внимание и на другое: barsik и murka — два разных объекта одного класса. У каждого своя копия полей, изменение одного не трогает другой.

Ключевое слово new и ссылки

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

Cat a = new Cat("Барсик", 3);
Cat b = a;        // b получает ту же ссылку, новый объект НЕ создается
b.age = 10;       // меняем возраст через b
System.out.println(a.age);   // печатает 10

Вывод:

10

Хотя мы меняли b.age, изменилось и a.age, потому что a и b — это две ссылки на один объект. new вызывался один раз, значит объект в памяти один. Сравните с ситуацией, где new вызван дважды:

Cat a = new Cat("Барсик", 3);
Cat b = new Cat("Барсик", 3);   // второй объект, отдельный
b.age = 10;
System.out.println(a.age);      // печатает 3

Вывод:

3

Здесь два независимых объекта, и изменение одного не влияет на другой, даже если кличка совпадает. Равенство == для таких переменных сравнивает именно ссылки (один это объект или разные), а не содержимое полей.

Частая ошибка: обращение к объекту, которого нет

Локальная переменная-ссылка не указывает ни на какой объект, пока вы не присвоили ей результат new. Попытка вызвать метод у неинициализированной локальной переменной не скомпилируется.

Неверно:

Cat cat;         // объявили ссылку, но объект не создали
cat.meow();      // обращаемся к несуществующему объекту

Результат компиляции (javac):

error: variable cat might not have been initialized
        cat.meow();
        ^

Исправление — создать объект через new до обращения к нему:

Cat cat = new Cat("Барсик", 3);
cat.meow();      // теперь объект есть, метод вызывается

Отдельно стоит знать про поля: поле-ссылка, которое вы не инициализировали, по умолчанию равно null (пустая ссылка). Обращение к нему компилируется, но падает уже при выполнении с NullPointerException. Поэтому и здесь правило простое: сначала new, потом обращение.

Выводы

  • Класс — это чертеж (описание полей и методов), объект — конкретный экземпляр по этому чертежу; одного класса хватает на сколько угодно объектов.
  • Поля хранят состояние объекта, методы задают его поведение; обращение к ним идет через точку у конкретного объекта.
  • Конструктор заполняет объект начальными данными при new, а this внутри него отличает поле объекта от одноименного параметра.
  • Переменная класса хранит ссылку на объект, а не сам объект: Cat b = a не создает новый объект, оба имени указывают на один экземпляр.
  • Объект появляется только после new; обращение к неинициализированной ссылке дает ошибку компиляции (локальная переменная) или NullPointerException (поле со значением null).

Где применяется / связь с практикой

Классы и объекты — это фундамент любого кода на Java: от простого консольного приложения до серверов и Android-приложений вы описываете сущности классами и работаете с их объектами. Понимание связки «класс — чертеж, объект — экземпляр, new — создание, this — текущий объект, ссылка — адрес» снимает большую часть ошибок новичка еще до знакомства с наследованием и интерфейсами.

Освойте тему на практике

Разобрать это на практике с проверкой кода наставником помогает курс Java Basic: там классы, объекты и ООП идут от простых примеров к рабочему проекту. Посмотреть формат занятий и задать вопрос преподавателю можно на открытых уроках Otus — они бесплатные и проходят регулярно.

FAQ

Можно ли создать объект без конструктора?
Да. Если вы не написали ни одного конструктора, компилятор добавляет конструктор по умолчанию без параметров, и new Cat() работает. Как только вы объявите свой конструктор с параметрами, конструктор по умолчанию исчезает, и вызывать нужно именно ваш.

В чем разница между полем и локальной переменной?
Поле объявляется внутри класса и живет столько же, сколько объект; у каждого объекта своя копия поля. Локальная переменная объявляется внутри метода, существует только во время его работы и не привязана к конкретному объекту.

Обязательно ли имя класса должно совпадать с именем файла?
Для публичного класса (public class Cat) — да: файл должен называться Cat.java. Это требование Java к публичным классам верхнего уровня, иначе компилятор выдаст ошибку.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)