Классы и объекты Java
Определите класс Java и создайте объекты из него — основа объектно-ориентированной модели Java.
Класс — это определение; объект — это экземпляр этого определения, существующий во время выполнения. Класс описывает как выглядит собака и что она умеет делать; объект — это Рекс, конкретная собака, живущая в памяти, пока работает ваша программа. Всё изложенное ниже строится на этом различии.
Определение класса
Определение класса находится в отдельном файле .java, названном по имени класса. Тело содержит поля (данные, которые несёт каждый экземпляр) и методы (поведение, которое может выполнять каждый экземпляр).
public class Dog {
String name;
int age;
void describe() {
System.out.println(name + ", age " + age);
}
}Это объявляет тип. Оно ещё не создаёт никаких собак — точно так же, как написание int не создаёт целое число.
Создание объекта с помощью new
Ключевое слово new просит JVM выделить место для одного экземпляра и вернуть ссылку на него:
Dog d = new Dog();На этой строке происходят три вещи:
- Выделение памяти. JVM находит место в куче, достаточное для
Dog. - Инициализация по умолчанию. Каждое поле получает значение по умолчанию —
nullдля объектов,0для int,falseдля boolean. - Вызов конструктора.
()вызывает конструктор. Мы не написали ни одного, поэтому Java предоставила конструктор без аргументов по умолчанию, который ничего дополнительного не делает. Следующая глава о конструкторах охватывает написание собственных.
После этого d хранит ссылку — небольшое значение, указывающее на объект в куче. Сам объект — это более крупная вещь в куче.
Чтение и запись полей
Доступ к полю осуществляется с помощью записи <ссылка>.<поле>:
d.name = "Rex";
d.age = 3;
System.out.println(d.name); // RexОбращение к полям также используется для вызова методов:
d.describe(); // Rex, age 3Точка одинакова в обоих случаях; является ли правая часть полем или методом, зависит только от того, что следует далее.
Ссылки — это не объекты
Это самое важное предложение в данной главе: переменная классового типа хранит ссылку, а не сам объект.
Dog a = new Dog();
a.name = "Rex";
Dog b = a; // copies the REFERENCE, not the dog
b.name = "Buddy";
System.out.println(a.name); // Buddya и b указывают на один и тот же объект в куче, поэтому изменение через одну из них видно через другую. Сравните с примитивами:
int x = 5;
int y = x; // copies the value
y = 9;
System.out.println(x); // still 5x и y независимы — примитивы копируются по значению. Глава о передаче по значению подробнее рассматривает, что это означает для аргументов методов.
null и NullPointerException
Ссылочная переменная может быть null, то есть «не указывает ни на какой объект»:
Dog d = null;
d.describe(); // NullPointerExceptionnull — это то, что вы получаете, если никогда не присвоили объект, или если поле объектного типа не было явно инициализировано. Разыменование null (запись d.something при d == null) генерирует NullPointerException во время выполнения. Написание кода, который не падает на null, — это половина практики Java.
NullPointerException — это ошибка времени выполнения, а не ошибка компиляции — код компилируется нормально и падает только тогда, когда эта строка действительно выполняется. Исправление состоит в том, чтобы убедиться, что переменная указывает на реальный объект (d = new Dog();), или защититься проверкой (if (d != null) d.describe();) перед разыменованием.Сравнение объектов: == и equals
Для ссылок == спрашивает «указывают ли эти две переменные на один и тот же объект?»:
Dog a = new Dog();
Dog b = new Dog();
Dog c = a;
System.out.println(a == b); // false — different objects
System.out.println(a == c); // true — same objectЕсли вы хотите сравнить содержимое двух объектов, вы вместо этого вызываете метод equals — каждый класс наследует метод по умолчанию от Object, но по умолчанию он просто проверяет равенство ссылок. Вы переопределяете equals, чтобы задать нужный смысл; см. главу equals & hashCode далее в этой части.
Один файл, много объектов
Одно определение класса поддерживает неограниченное количество экземпляров. Все они разделяют одну структуру, но каждый имеет собственное состояние:
Dog rex = new Dog(); rex.name = "Rex"; rex.age = 3;
Dog molly = new Dog(); molly.name = "Molly"; molly.age = 7;
rex.describe(); // Rex, age 3
molly.describe(); // Molly, age 7Метод вроде describe читает из this — объекта, на котором он вызван, — поэтому один и тот же код выводит разный результат в зависимости от того, на каком экземпляре он вызван.
Где живут классы
Файл .java, объявляющий public class Dog, должен называться Dog.java. В одном файле можно иметь непубличные классы, но только один public класс на файл, и его имя должно совпадать с именем файла. В небольших программах этой главы мы часто будем помещать вспомогательные классы в тот же файл, что и main, используя вложенные классы static, просто чтобы пример умещался в одном файле.
Рабочий пример
Что дальше
Вы можете объявить класс и создать его экземпляр, но пока что единственное поле, которое вы написали, принимало «любое значение». Следующая глава об атрибутах класса подробно рассматривает поля — объявление, значения по умолчанию, область видимости экземпляра и класса, а также соглашения, которым следует код Java.