🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
Всем привет! Сегодня мы затронем широкий круг тем, связанных с процессом создания объектов.

Мы проанализируем этот процесс от начала до конца, изучим, как вызываются конструкторы, как и в каком порядке инициализируются поля (в том числе статические) и т. д.
Небольшое введение
Для начала вспомним, как создается объект.
С точки зрения разработчика этот процесс довольно простой: создаём класс, пишем new
, и все готово 🙂 А что происходит внутри компьютера и Java-машины (JVM)? Например, мы пишем:
Cat cat = new Cat();
- Сначала выделяется память для хранения объекта.
- Затем Java-машина создает ссылку на объект (в нашем случае это кошка).
- Наконец, переменные инициализируются и вызывается конструктор (мы рассмотрим этот процесс более подробно).
Важно: объект существует до тех пор, пока есть хотя бы одна ссылка на него. Если их не остается, то объект становится добычей сборщика мусора.
Особого внимания заслуживает третий пункт: он представляет собой целый набор операций, выполняемых в строгом порядке.
Чтобы понять, в каком порядке что происходит, создадим два простых класса – родительский и дочерний:
public class Vehicle {
public static int vehicleCounter = 0;
private String description = "Vehicle";
public Vehicle() {
}
public String getDescription() {
return description;
}
}
public class Truck extends Vehicle {
private static int truckCounter = 0;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
}
Run CodeКласс Truck
позволяет создать совокупность характеристик грузового автомобиля; поля класса содержат год выпуска, модель транспорта и максимальную скорость (см. строки 18-20).
Допустим, мы хотим создать такой объект:
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
}
Run CodeДля Java-машины этот процесс будет выглядеть совершенно другим образом.
Шаг 1
Сначала, до вызова конструкторов, инициализируются статические переменные класса Vehicle
. Обратите внимание, что процесс начинается не с дочернего, а с родительского класса. Чтобы подтвердить это, установим поле vehicleCounter
в классе Vehicle
(строка 3) и попробуем вывести его значение как в конструкторах Vehicle
, так и в конструкторах Truck
.
public class Vehicle {
public static int vehicleCounter = 10;
private String description = "Vehicle";
public Vehicle() {
System.out.println(vehicleCounter);
}
public String getDescription() {
return description;
}
}
public class Truck extends Vehicle {
private static int truckCounter = 0;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
System.out.println(vehicleCounter);
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCount++;
}
}
Run CodeМы специально поместили оператор println в самое начало конструктора Truck
, чтобы убедиться, что на момент вывода vehicleCounter
поля еще не были инициализированы.
Вот результат:
10
10
Шаг 2
После инициализации статических переменных родительского класса инициализируются статические переменные дочернего класса. В нашем случае это поле truckCounter
класса Truck
(строка 17).
Проведем еще один эксперимент: попробуем вывести значение truckCounter
внутри конструктора Truck
до инициализации других полей.
public class Truck extends Vehicle {
private static int truckCounter = 10;
private int yearOfManufacture;
private String model;
private int maxSpeed;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
System.out.println(truckCounter);
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
}
Run CodeИтак, значение 10 уже присвоено нашей статической переменной в начале работы конструктора Truck
.
Шаг 3
Далее инициализируются нестатические переменные родительского класса.
Эксперимента ради присвоим некоторое начальное значение переменной description
в классе Vehicle
, а затем изменим его в конструкторе:
public class Vehicle {
public static int vehicleCounter = 10;
private String description = "Initial value of the description field";
public Vehicle() {
System.out.println(description);
description = "Vehicle";
System.out.println(description);
}
public String getDescription() {
return description;
}
}
Run CodeЗапустив метод main()
, создадим объект класса Truck
:
public class Main {
public static void main(String[] args) throws IOException {
Truck truck = new Truck(2017, "Scania S 500 4x2", 220);
}
}
Run CodeВ качестве результата получим:
Initial value of the description field
Vehicle
Это доказывает, что в момент начала работы конструктора Vehicle
полю description
уже присвоено значение.
Шаг 4
Итак, пришло время конструкторов! Сначала вызывается конструктор родительского класса, что тоже можно легко проверить.
Попробуем вывести в консоль две строки: одну зададим внутри конструктора родительского класса Vehicle
, вторую – внутри конструктора Truck
. Предполагается, что строка, заданная внутри Vehicle
, будет выведена первой:
public Vehicle() {
System.out.println("Hello from the Vehicle constructor!");
}
public Truck(int yearOfManufacture, String model, int maxSpeed) {
System.out.println("Hello from the Truck constructor!");
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
Run CodeЗапустим метод main()
и посмотрим на результат:
Hello from the Vehicle constructor!
Hello from the Truck constructor!
Наша гипотеза оказалась верной 🙂
Шаг 5
Настало время для инициализации нестатических полей дочернего класса (в нашем случае – класса Truck
).
Присвоим переменной maxSpeed
некоторое начальное значение и в конструкторе Truck
проверим, что значение было присвоено еще до запуска конструктора:
public class Truck extends Vehicle {
private static int truckCounter = 10;
private int yearOfManufacture;
private String model;
private int maxSpeed = 150;
public Truck(int yearOfManufacture, String model, int maxSpeed) {
System.out.println("Initial value of maxSpeed = " + this.maxSpeed);
this.yearOfManufacture = yearOfManufacture;
this.model = model;
this.maxSpeed = maxSpeed;
Vehicle.vehicleCounter++;
truckCounter++;
}
}
Run CodeКонсольный вывод:
Initial value of maxSpeed = 150
Действительно, когда запускается конструктор Truck
, значение maxSpeed
уже равно 150!
Шаг 6
Наконец, только на последнем шаге вызывается конструктор дочернего класса Truck
. Полям объекта присваиваются значения, которые мы передадим в конструктор.
Процесс создания объекта – дело нелегкое, но мы довольно детально изучили, что происходит “под капотом” 🙂
Для вас также быть полезной статья «Классы и объекты в Java»
Перевод статьи «Sequence of actions during object creation».