JavaRush /Курсы /JAVA 25 SELF /Создание иерархии классов, примеры из жизни

Создание иерархии классов, примеры из жизни

JAVA 25 SELF
17 уровень , 3 лекция
Открыта

1. Что такое иерархия классов?

В программировании (и не только в нём) иерархия — это древовидная структура, где у нас есть «общие» сущности на вершине дерева (базовые классы), а ниже — более конкретные (подклассы). В Java иерархия классов строится с помощью наследования: каждый подкласс может сам быть родителем для других подклассов и так далее.

Аналогия:

  • Животное (Animal) — это общий класс.
  • Млекопитающее (Mammal) — это частный случай животного.
  • Собака (Dog) — это частный случай млекопитающего.
  • А вот конкретный «Шарик» — это уже объект класса Dog.

В коде это выглядит так:

class Animal { }
class Mammal extends Animal { }
class Dog extends Mammal { }

Иерархия классов позволяет описывать общие свойства и поведение «наверху», а детали — «внизу». Это делает код более логичным и избавляет от дублирования.

Схема иерархии

Animal
├── Mammal
│   ├── Dog
│   └── Cat
└── Bird
    └── Sparrow

2. Как строить иерархию: логика и практика

Определяем общее и частное

Главное правило: базовый класс должен содержать то, что характерно для всех его потомков. А вот всё уникальное — переносим в подклассы.

Пример:

  • Все животные могут дышать и есть — значит, методы breathe() и eat() должны быть в классе Animal.
  • Только птицы умеют летать — значит, метод fly() будет в классе Bird, а не в Animal.
  • Только собаки умеют лаять — метод bark() будет в классе Dog.

Пример: Животные

// Базовый класс
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    void eat() {
        System.out.println(name + " ест.");
    }

    void makeSound() {
        System.out.println(name + " издаёт звук.");
    }
}

// Подкласс: Млекопитающее
class Mammal extends Animal {
    Mammal(String name) {
        super(name);
    }

    void feedMilk() {
        System.out.println(name + " кормит детёнышей молоком.");
    }
}

// Подкласс: Собака
class Dog extends Mammal {
    Dog(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " лает: Гав-гав!");
    }

    void wagTail() {
        System.out.println(name + " виляет хвостом.");
    }
}

// Подкласс: Кошка
class Cat extends Mammal {
    Cat(String name) {
        super(name);
    }

    @Override
    void makeSound() {
        System.out.println(name + " мяукает: Мяу!");
    }

    void purr() {
        System.out.println(name + " мурлычет.");
    }
}

// Подкласс: Птица
class Bird extends Animal {
    Bird(String name) {
        super(name);
    }

    void fly() {
        System.out.println(name + " летает.");
    }

    @Override
    void makeSound() {
        System.out.println(name + " чирикает: Чик-чирик!");
    }
}

Вызов в main:

public class ZooDemo {
    public static void main(String[] args) {
        Dog sharik = new Dog("Шарик");
        Cat murka = new Cat("Мурка");
        Bird sparrow = new Bird("Воробей");

        sharik.eat();        // Шарик ест.
        sharik.makeSound();  // Шарик лает: Гав-гав!
        sharik.feedMilk();   // Шарик кормит детёнышей молоком.
        sharik.wagTail();    // Шарик виляет хвостом.

        murka.eat();         // Мурка ест.
        murka.makeSound();   // Мурка мяукает: Мяу!
        murka.feedMilk();    // Мурка кормит детёнышей молоком.
        murka.purr();        // Мурка мурлычет.

        sparrow.eat();       // Воробей ест.
        sparrow.makeSound(); // Воробей чирикает: Чик-чирик!
        sparrow.fly();       // Воробей летает.
    }
}

Визуализация: дерево классов

Класс Родитель Особенности
Animal
Object
name, eat(), makeSound()
Mammal
Animal
feedMilk()
Dog
Mammal
makeSound(), wagTail()
Cat
Mammal
makeSound(), purr()
Bird
Animal
fly(), makeSound()

3. Ещё примеры из жизни

Геометрические фигуры

Иерархии классов отлично подходят для моделирования геометрии.

// Базовый класс
class Shape {
    void draw() {
        System.out.println("Рисуем фигуру.");
    }
}

// Круг
class Circle extends Shape {
    double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    void draw() {
        System.out.println("Рисуем круг радиусом " + radius);
    }
}

// Прямоугольник
class Rectangle extends Shape {
    double width, height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    void draw() {
        System.out.println("Рисуем прямоугольник " + width + "x" + height);
    }
}

Использование:

public class ShapeDemo {
    public static void main(String[] args) {
        Shape s1 = new Circle(5);
        Shape s2 = new Rectangle(3, 4);

        s1.draw(); // Рисуем круг радиусом 5.0
        s2.draw(); // Рисуем прямоугольник 3.0x4.0
    }
}

Транспорт

class Vehicle {
    void move() {
        System.out.println("Транспорт движется.");
    }
}

class Car extends Vehicle {
    @Override
    void move() {
        System.out.println("Машина едет по дороге.");
    }
}

class Bicycle extends Vehicle {
    @Override
    void move() {
        System.out.println("Велосипедист крутит педали.");
    }
}

main:

Vehicle v1 = new Car();
Vehicle v2 = new Bicycle();

v1.move(); // Машина едет по дороге.
v2.move(); // Велосипедист крутит педали.

Пользователи

class User {
    String username;
    User(String username) { this.username = username; }
    void login() { System.out.println(username + " вошёл в систему."); }
}

class Admin extends User {
    Admin(String username) { super(username); }
    void deleteUser(String user) {
        System.out.println(username + " удалил пользователя " + user);
    }
}

class Customer extends User {
    Customer(String username) { super(username); }
    void buy() { System.out.println(username + " совершил покупку."); }
}

4. Полезные нюансы

Не злоупотребляйте наследованием. Наследование — инструмент для отношений «is-a». Если вы хотите сказать «Кошка — это Животное», используйте наследование. Если же «Кошка содержит Хвост», используйте композицию (has-a).

Плохо:

class Engine { /* ... */ }
class Car extends Engine { /* Машина — это двигатель? Нет, это композиция! */ }

Хорошо:

class Car {
    Engine engine; // Машина содержит двигатель
}

Не делайте слишком глубоких иерархий. Чем больше уровней — тем сложнее поддерживать и понимать код. Обычно 2–3 уровня — это максимум для большинства задач.

Не делайте слишком «плоских» иерархий. Если у вас есть 20 классов, все наследуются напрямую от одного базового, возможно, стоит пересмотреть архитектуру.

Наследование «ради удобства». Иногда хочется «украсть» методы или поля, не задумываясь о логике. Это путь к хаосу! Если class A и class B не связаны логически, не нужно делать class B extends A только ради пары методов.

Нарушение принципа подстановки Лисков. Если подкласс не может быть использован вместо родителя без сюрпризов — иерархия построена неверно.

Дублирование кода. Если вы ловите себя на мысли «ой, а этот метод копируется из класса в класс», возможно, пора вынести его в базовый класс.

5. Типичные ошибки при построении иерархий классов

Ошибка №1: Наследование без логики «is-a».
Если вы используете наследование только ради доступа к методам или полям, а не потому, что подкласс действительно «является» родителем, ваш код быстро превратится в кашу. Например, «Машина наследует Двигатель» — это не is-a, а has-a.

Ошибка №2: Игнорирование уникальных особенностей подкласса.
Если все ваши подклассы выглядят одинаково и не добавляют ничего нового, возможно, иерархия не нужна — используйте один класс.

Ошибка №3: Дублирование кода в подклассах.
Если вы копируете одну и ту же реализацию в несколько подклассов, стоит вынести её в родительский класс.

Ошибка №4: Слишком сложная или глубокая иерархия.
Многоуровневые иерархии сложно поддерживать и тестировать. Чем проще — тем лучше!

Ошибка №5: Переопределение методов без аннотации @Override.
Без аннотации легко ошибиться в сигнатуре метода, и тогда метод не будет переопределён, а будет считаться новым. Всегда используйте @Override!

1
Задача
JAVA 25 SELF, 17 уровень, 3 лекция
Недоступна
Виртуальный питомец: Бобик ест 🐕
Виртуальный питомец: Бобик ест 🐕
1
Задача
JAVA 25 SELF, 17 уровень, 3 лекция
Недоступна
Автошкола: Движение и сигналы 🚦
Автошкола: Движение и сигналы 🚦
1
Задача
JAVA 25 SELF, 17 уровень, 3 лекция
Недоступна
Школьная иерархия: Кто я? 🎓
Школьная иерархия: Кто я? 🎓
1
Задача
JAVA 25 SELF, 17 уровень, 3 лекция
Недоступна
Зоологическая энциклопедия: Особенности кошек 🐈
Зоологическая энциклопедия: Особенности кошек 🐈
Комментарии (2)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ksanders Уровень 32
26 ноября 2025
В очередной раз пишу о том, насколько же задачи однообразные, даже ни одного конструктора не используется на все 4 задания, в тексте лекции и то код сложнее 😕
Xaxatumba Уровень 38
11 ноября 2025
Нда всё как обычно. is-a, has-a, принцип подстановки Лисков (LSP). Должно соответствовать, а что это такое ищите сами это к лекции не относиться.