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

Эта тема не будет сложной: с наследованием и примерами наследования мы уже рассказывали в прошлых статьях. Сегодня наша главная цель – закрепить полученные знания, более подробно рассмотреть механизм наследования и еще раз пробежаться по примерам. 🙂
Что ж, поехали!
Наследование в Java и его преимущества
Как вы наверняка помните, наследование – это механизм, позволяющий описать новый класс на основе существующего класса (родительского). При этом новый класс перенимает свойства и функциональность родительского класса.
Давайте вспомним пример наследования, который мы разбирали в предыдущих статьях:
public class Car {
private String model;
private int maxSpeed;
private int yearOfManufacture;
public Car(String model, int maxSpeed, int yearOfManufacture) {
this.model = model;
this.maxSpeed = maxSpeed;
this.yearOfManufacture = yearOfManufacture;
}
public void gas() {
// Газ
}
public void brake() {
// Тормоз
}
}
public class Truck extends Car {
public Truck(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
public class Sedan extends Car {
public Sedan(String model, int maxSpeed, int yearOfManufacture) {
super(model, maxSpeed, yearOfManufacture);
}
}
Run CodeУ нас есть определенная программа, которая предполагает работу с различными типами автомобилей. Даже если вы не являетесь автолюбителем, вы наверняка знаете, что в мире существует огромное количество типов автомобилей. 🙂
Поэтому общие свойства всех машин мы вынесли в родительский класс Car
. Итак, что же объединяет все машины, независимо от их разновидности?
У каждого автомобиля есть год выпуска, название модели и максимальная скорость. Эти характеристики мы разместили в полях model
, maxSpeed
и yearOfManufacture
. Что касается поведения – любая машина умеет разгоняться и тормозить 🙂 Эти действия мы описали в методах gas()
и brake()
.
Зачем нужно наследование?
Какие преимущества это нам дает? Прежде всего, это уменьшает количество кода.
Конечно, можно было бы обойтись без родительского класса. Но тогда в каждом классе Truck
, Sedan
, F1Car
и SportsCar
пришлось бы заново писать методы gas()
и brake()
. А еще создавать поля model
, maxSpeed
и yearOfManufacture
. Представьте, сколько бы мы набрали лишнего кода, если в проекте десятки типов авто.

Когда у нас есть несколько десятков классов автомобилей, количество дублирующегося кода становится действительно серьезным.
Вынося общие поля и методы (их ещё называют состояния и поведения) в родительский класс, мы серьезно экономим и время, и место. А если какой-то тип машины имеет уникальные свойства или методы, которых нет у других? Нет проблем! Их можно спокойно добавить в дочерний класс.
Пример:
public class F1Car extends Car {
public void pitStop() {
// Только гоночные машины делают пит-стопы
}
public static void main(String[] args) {
F1Car formula1Car = new F1Car();
formula1Car.gas();
formula1Car.pitStop();
formula1Car.brake();
}
}
Run CodeНа примере болида Формулы-1 видно: в отличие от других машин, у него есть особенное поведение – пит-стоп.
Это не мешает общей структуре наследования: общие действия описаны в родительском Car
, а специфичные методы можно спокойно добавлять в потомке. То же самое и с полями: если у дочернего класса появляются уникальные свойства, мы просто объявляем их внутри него – и все.

То же самое касается и полей: если у дочернего класса есть уникальные свойства, мы спокойно объявляем эти поля внутри дочернего класса и перестаем беспокоиться 🙂 Возможность повторного использования кода – главное преимущество наследования.
Для программиста это особенно важно: писать как можно меньше лишнего кода. С этим принципом вы еще не раз столкнетесь на практике.
Особенности наследования в Java
Еще одна важная вещь, которую нужно запомнить – в Java нет множественного наследования. Каждый класс может наследовать только один родительский класс. Почему так устроено, мы обсудим позже. Сейчас просто стоит это запомнить.
С наследованием все более-менее понятно. Давайте двигаться дальше.
Композиция и агрегация
Классы и объекты могут быть связаны друг с другом. Наследование описывает связь типа “является“. Например: лев — это животное. Такую связь легко выразить через наследование, где Animal
– родительский класс, а Lion
– дочерний.
Однако не все отношения описываются таким образом. Например, клавиатура определенно связана с компьютером, но это не компьютер. Руки как-то связаны с человеком, но являются человеком.
В таких случаях связь описывается по-другому: не “является“, а “имеет“. Рука не является человеком, но является частью человека. Клавиатура не является компьютером, но является его частью.
Такую “has-a” связь можно выразить в коде с помощью агрегации и композиции. Главное отличие между ними – в “строгости” этой связи.
Приведем простой пример: у нас есть класс Car
. У каждого автомобиля есть двигатель. Кроме того, в каждом автомобиле есть пассажиры.
У нас есть класс Car
. У каждого автомобиля есть двигатель. Кроме того, в каждом автомобиле есть пассажиры.
В чем принципиальная разница между движком Engine
и полями пассажиров Passenger[]
? Если в машине сидит пассажир А, это не мешает сидеть в ней пассажирам В и С. Машина может одновременно везти несколько пассажиров. Более того, если все пассажиры выйдут из автомобиля, он все равно будет работать без сбоев.
Отношения между классом Car
и массивом Passenger[] passengers
менее строгие. Такую связь называют агрегацией.
Другой пример агрегации: предположим, у нас есть класс Student
и класс StudentGroup
. Студент может вступить в несколько студенческих организаций: клуб физиков, клуб фанатов “Звездных войн” и/или студенческий комедийный клуб.
Композиция – это более строгий вид отношений. Когда мы говорим о композиции, объект является частью другого объекта и не может одновременно принадлежать другому объекту того же типа.
Самый простой пример – двигатель автомобиля. Двигатель является частью автомобиля и не может быть частью другого автомобиля. Как видите, их отношения гораздо строже, чем отношения между Car
и Passengers
.

Теперь вы знаете, как классы могут быть связаны друг с другом с помощью наследования, композиции и агрегации. Эти механизмы помогают строить более гибкие и понятные программы. Дальше будет еще интереснее!
Перевод статьи «Relationships between classes. Inheritance, composition, and aggregation».