Объектно-ориентированное программирование (ООП) — это способ строить программу из объектов: самостоятельных сущностей, которые хранят свои данные и умеют выполнять действия над ними. В основе подхода лежат четыре принципа (их часто называют столпами): инкапсуляция, наследование, полиморфизм и абстракция.
Содержание
Принципы эти язык-агностичны: они одинаково работают в Java, C#, Python, C++ и других языках, меняется только синтаксис. Ниже разберем каждый столп, покажем минимальный код на Java и разведем пары понятий, которые путают чаще всего: класс и объект, инкапсуляцию и абстракцию, наследование и полиморфизм.
Класс и объект: в чем разница
Класс — это описание, шаблон: какие данные и какие действия будут у объектов этого типа. Объект — конкретный экземпляр, созданный по этому шаблону и занимающий место в памяти. Аналогия: класс — чертеж дома, объект — построенный по нему дом. По одному чертежу можно построить много домов, и у каждого будут свои значения полей.
class Cat { // класс - чертеж
String name; // поле (атрибут)
void meow() { // метод (поведение)
System.out.println(name + ": мяу");
}
}
Cat murka = new Cat(); // объект - конкретный экземпляр
murka.name = "Мурка";
murka.meow(); // напечатает: Мурка: мяу
Поля (атрибуты) хранят состояние объекта, методы задают его поведение. В примере name — поле, meow() — метод, а murka — объект класса Cat.
Четыре принципа ООП
Четыре столпа отвечают на разные вопросы проектирования. Сводная таблица — суть каждого принципа и что он дает на практике.
| Принцип | Суть | Что дает |
|---|---|---|
| Инкапсуляция | Данные и работающие с ними методы собраны в одном объекте, доступ к внутренностям ограничен | Защита состояния от случайной порчи, контроль инвариантов |
| Наследование | Новый класс берет поля и методы существующего и дополняет их | Переиспользование кода, общий тип для родственных классов |
| Полиморфизм | Один вызов метода срабатывает по-разному в зависимости от типа объекта | Единый интерфейс для разных реализаций, расширяемость |
| Абстракция | В модели остаются только существенные для задачи свойства, детали скрыты | Простота использования, работа через понятия предметной области |
Инкапсуляция
Инкапсуляция — это объединение данных и методов в одном объекте вместе с ограничением прямого доступа к внутренним данным. Снаружи объект дергают через публичные методы, а поля прячут (в Java — модификатором private). Так объект сам следит за корректностью своего состояния.
class Account {
private long balance; // поле скрыто, снаружи не изменить напрямую
public void deposit(long sum) {
if (sum > 0) { // инвариант: пополнение только положительное
balance += sum;
}
}
public long getBalance() {
return balance;
}
}
Здесь нельзя присвоить balance отрицательное значение мимо проверки: поле закрыто, а метод deposit пропускает только корректные суммы. Без инкапсуляции любой участок кода мог бы записать в баланс что угодно, и найти источник ошибки было бы тяжело.
Наследование
Наследование позволяет создать класс на основе существующего: потомок получает поля и методы родителя, а затем добавляет свои или переопределяет унаследованные. В Java для этого служит ключевое слово extends. Отношение читается как «является»: автобус является транспортным средством.
class Vehicle {
protected int speed;
void move() {
System.out.println("Двигаюсь со скоростью " + speed);
}
}
class Bus extends Vehicle { // Bus наследует поле speed и метод move
int seats = 45; // добавили новое поле
}
Объект Bus умеет вызывать move(), хотя этот метод описан в Vehicle. Наследование стоит применять именно для отношения «является», а не «содержит»: если у автобуса есть двигатель, двигатель лучше сделать отдельным полем-объектом, а не родителем.
Полиморфизм
Полиморфизм — это возможность вызывать один и тот же метод у объектов разных классов и получать поведение, соответствующее конкретному типу. Какая именно версия метода отработает, определяется во время выполнения по фактическому типу объекта (динамическая диспетчеризация).
class Shape {
double area() { return 0; }
}
class Circle extends Shape {
double r = 2;
@Override double area() { return Math.PI * r * r; }
}
class Square extends Shape {
double side = 3;
@Override double area() { return side * side; }
}
Теперь по ссылке типа Shape можно работать с любой фигурой, не зная ее конкретный класс:
Shape s = new Circle();
System.out.println(s.area()); // 12.566370614359172
s = new Square();
System.out.println(s.area()); // 9.0
Вызов один и тот же — s.area(), а результат зависит от того, что реально лежит в s. Это упрощает расширение: чтобы добавить треугольник, достаточно нового класса-наследника, старый код трогать не нужно.
Абстракция
Абстракция — это выделение только существенных для задачи свойств объекта и скрытие несущественных деталей за простым понятием. В коде она часто выражается абстрактными классами и интерфейсами: они задают, что объект умеет, но не как именно. В Java абстрактный класс объявляют словом abstract, а абстрактный метод — без тела.
abstract class Payment {
abstract void pay(long amount); // что делаем, без реализации «как»
}
class CardPayment extends Payment {
@Override void pay(long amount) {
System.out.println("Оплата картой: " + amount);
}
}
Код, который принимает платежи, работает с типом Payment и вызывает pay(), не вникая, картой это, наличными или переводом. Абстракция задает контракт, а конкретные классы его выполняют.
Инкапсуляция и абстракция: не путать
Эти два принципа связаны, но отвечают на разные вопросы.
- Инкапсуляция — про то, как объект защищает свои данные: поля закрыты, снаружи доступен только контролируемый набор методов. Это механизм сокрытия состояния.
- Абстракция — про то, что показать наружу как модель: какие понятия и операции важны для задачи. Это выбор уровня детализации.
Коротко: инкапсуляция скрывает внутренние данные, абстракция скрывает сложность реализации за простым понятием. В классе Account закрытое поле balance — инкапсуляция; сама идея «счет умеет пополняться и показывать баланс» без подробностей хранения — абстракция.
Наследование и полиморфизм: не одно и то же
Наследование и полиморфизм тоже часто смешивают, потому что в примерах они идут вместе.
- Наследование — это способ переиспользовать код и выстроить иерархию типов (потомок берет члены родителя).
- Полиморфизм — это выбор нужной реализации в момент вызова по фактическому типу объекта.
Наследование — один из механизмов, который включает полиморфизм, но не единственный: в Java полиморфизм работает и через интерфейсы без наследования класса. Наследование отвечает на вопрос «откуда берутся члены класса», полиморфизм — на вопрос «чей метод вызовется».
Зачем нужен ООП
ООП удобно, когда программу естественно описать как набор взаимодействующих сущностей: пользователи, заказы, платежи, фигуры на экране. Основные выгоды подхода:
- Модульность: логика и данные сущности лежат в одном месте, изменения локальны.
- Переиспользование: общее поведение выносится в родителя или интерфейс, не дублируется.
- Расширяемость: новый вариант добавляется новым классом, старый код часто остается нетронутым.
ООП — не универсальная замена другим подходам. Для задач, где в центре вычисления и преобразования данных, а не сущности с поведением, функциональный или процедурный стиль бывает проще и короче. Многие языки (Java, C#, Python) поддерживают несколько парадигм, и их комбинируют.
Выводы
- ООП строит программу из объектов, каждый из которых хранит данные и поведение; класс — это шаблон, объект — его конкретный экземпляр.
- Четыре принципа: инкапсуляция (защита данных), наследование (переиспользование через иерархию), полиморфизм (единый вызов, разное поведение), абстракция (модель без деталей реализации).
- Инкапсуляция скрывает внутренние данные, абстракция скрывает сложность за простым понятием — это разные принципы.
- Наследование дает члены родителя, полиморфизм выбирает реализацию по типу объекта; наследование — лишь один из путей к полиморфизму.
- ООП силен для предметных областей с сущностями и поведением, но не обязателен там, где в центре вычисления.
Где применяется / связь с практикой
Принципы ООП — это база для промышленной разработки на Java, C#, Kotlin, Python и большинстве современных языков. На них держатся типовые архитектурные приемы (слои, сервисы, репозитории) и почти все популярные фреймворки, поэтому без понимания четырех столпов трудно читать чужой код и проектировать свой.
Освойте тему на практике
Разобрать инкапсуляцию, наследование, полиморфизм и абстракцию на живых задачах с проверкой наставника удобно на курсе Java Basic. Если хотите сперва посмотреть формат и уровень, загляните на открытые уроки Otus — там разбирают темы вживую и отвечают на вопросы.
FAQ
Обязательно ли использовать все четыре принципа сразу?
Нет, это не чек-лист. Инкапсуляция применяется почти всегда, а наследование и полиморфизм — по необходимости; навязывать иерархию там, где ее нет, вредно.
Чем интерфейс отличается от абстрактного класса в Java?
Абстрактный класс может хранить поля и готовые методы и наследуется только один, а интерфейс задает набор методов-обязательств, и класс может реализовать сразу несколько интерфейсов.
Можно ли писать на ООП-языке без объектов?
Да, короткий скрипт может обойтись статическими методами и процедурным стилем. ООП раскрывается на задачах, где есть сущности со своим состоянием и поведением.



