Три принципа ООП в Java
Три принципа ООП в Java — это инкапсуляция, наследование и полиморфизм. Часто к ним добавляют четвёртый — абстракцию, поэтому в литературе встречаются оба варианта: «три принципа» и «четыре принципа» объектно-ориентированного программирования. Ниже — что означает каждый из них и как он выглядит в коде на Java.
Что такое ООП и зачем оно нужно
Объектно-ориентированное программирование (ООП) — это подход, при котором программа описывается как набор взаимодействующих объектов. Объект хранит состояние (поля) и поведение (методы), а класс задаёт шаблон, по которому объекты создаются.
В отличие от процедурного подхода, где данные и функции живут отдельно друг от друга, ООП связывает их вместе. Это даёт три практических выгоды: код проще менять локально, поведение можно расширять без правки готовых классов и один и тот же вызов может работать с разными реализациями.
Java спроектирована вокруг классов: любой метод обязан находиться внутри класса или интерфейса, а точка входа main — тоже метод класса.
Инкапсуляция
Инкапсуляция — это свойство системы объединять данные и работающие с ними методы в одном классе и скрывать детали реализации от внешнего кода. Внешний код видит только публичный контракт, а как устроено внутри — не его забота.
В Java инкапсуляция обеспечивается модификаторами доступа (private, protected, package-private, public) и доступом к полям через методы.
public class BankAccount {
private long balance; // состояние скрыто: снаружи поле не видно
public void deposit(long amount) {
if (amount <= 0) { // инвариант класса защищён
throw new IllegalArgumentException("Сумма должна быть положительной");
}
balance += amount;
}
public long getBalance() {
return balance;
}
} BankAccount account = new BankAccount();
account.deposit(500);
// account.balance = -100; // не скомпилируется: поле private
System.out.println(account.getBalance()); // 500 Ключевая мысль: инкапсуляция не про «поставить private», а про то, что класс сам отвечает за корректность своих данных. Отрицательный баланс просто невозможно создать извне.
Важно
Класс, у которого все поля private, но на каждое поле механически сгенерированы геттер и сеттер, инкапсулированным по сути не является: состояние по-прежнему меняется снаружи произвольно. Сокрытие данных — средство, а цель — сохранить инварианты объекта.
Наследование
Наследование — это свойство системы описывать новый класс на основе существующего, полностью или частично заимствуя его функциональность. Класс, от которого наследуют, называют базовым, родительским или суперклассом; новый класс — потомком, наследником или производным классом.
В Java наследование объявляется словом extends, а обратиться к реализации родителя можно через super.
public class Employee {
protected final String name;
public Employee(String name) {
this.name = name;
}
public double salary() {
return 1000;
}
}
public class Manager extends Employee {
public Manager(String name) {
super(name); // вызов конструктора суперкласса
}
@Override
public double salary() {
return super.salary() * 1.5; // переиспользуем логику родителя
}
} Важные ограничения Java:
- класс наследуется только от одного класса — множественного наследования классов нет;
- реализовать при этом можно сколько угодно интерфейсов;
- все классы неявно наследуются от
java.lang.Object; - класс с модификатором
finalунаследовать нельзя (например,String).
Наследование или композиция
Наследование стоит применять, когда потомок действительно «является» частным случаем родителя (Manager — это Employee). Если нужно просто переиспользовать чужой код, лучше композиция: класс хранит другой объект в поле и делегирует ему работу. Наследование жёстко связывает классы, и изменение суперкласса ломает всех потомков.
Полиморфизм
Полиморфизм буквально означает «много форм». Это свойство системы работать с объектами через единый интерфейс, не зная их конкретного типа и внутреннего устройства. Классическая формулировка — «один интерфейс, множество реализаций».
В Java различают две формы полиморфизма.
Динамический полиморфизм (переопределение)
Реализуется через переопределение методов: какой метод вызвать, решается во время выполнения по фактическому типу объекта (динамическое связывание).
List<Employee> staff = List.of(
new Employee("Иван"),
new Manager("Ольга"));
for (Employee employee : staff) {
// тип переменной — Employee, но вызывается метод фактического класса
System.out.println(employee.salary());
}
// 1000.0
// 1500.0 Статический полиморфизм (перегрузка)
Реализуется через перегрузку: в классе несколько методов с одним именем и разными параметрами, а нужный выбирает компилятор по типам аргументов.
public class Printer {
public void print(int value) {
System.out.println("int: " + value);
}
public void print(String value) {
System.out.println("String: " + value);
}
} Вопрос с собеседования
Перегрузка разрешается на этапе компиляции по объявленному типу аргумента, переопределение — на этапе выполнения по реальному типу объекта. Поэтому статические методы не переопределяются: они скрываются (method hiding) и выбираются по типу ссылки, а не объекта.
Подробный разбор термина за пределами Java — в статье Полиморфизм (информатика).
Абстракция — четвёртый принцип
Абстракция данных — это выделение значимых характеристик объекта и отбрасывание незначимых для решаемой задачи. Набор таких характеристик и есть абстракция.
Одна и та же сущность абстрагируется по-разному в зависимости от контекста: для платёжного сервиса у клиента важны идентификатор и способ оплаты, а для отдела кадров того же человека описывают должность и стаж.
В Java абстракцию выражают интерфейсы и абстрактные классы: они описывают, что объект умеет делать, ничего не говоря о том, как именно.
public interface Payment {
void pay(long amount); // что делаем — известно
}
public class CardPayment implements Payment {
@Override
public void pay(long amount) { // как делаем — деталь реализации
System.out.println("Оплата картой: " + amount);
}
}
public class CashPayment implements Payment {
@Override
public void pay(long amount) {
System.out.println("Оплата наличными: " + amount);
}
} Payment payment = new CardPayment(); // код работает с абстракцией
payment.pay(2500); // Оплата картой: 2500 Обратите внимание: этот пример одновременно демонстрирует абстракцию (интерфейс Payment) и полиморфизм (вызов pay() уходит в нужную реализацию). Принципы ООП работают в связке, а не по отдельности.
Сравнение четырёх принципов
| Принцип | Что означает | Чем реализуется в Java | Что даёт на практике |
|---|---|---|---|
| Инкапсуляция | Данные и методы объединены, детали скрыты | private, protected, геттеры и сеттеры, package-private | Объект нельзя привести в некорректное состояние извне |
| Наследование | Новый класс строится на основе существующего | extends, super, Object как общий предок | Повторное использование и расширение поведения |
| Полиморфизм | Один интерфейс — много реализаций | Переопределение, перегрузка, интерфейсы, дженерики | Код не зависит от конкретных классов |
| Абстракция | Отбор значимых характеристик, сокрытие лишнего | interface, abstract class | Модель предметной области без лишних деталей |
ООП и SOLID — это разные вещи
Принципы ООП описывают, из чего состоит объектная модель. SOLID — это пять принципов проектирования, которые подсказывают, как пользоваться ООП, чтобы код оставался поддерживаемым. Их часто путают на собеседованиях, поэтому полезно держать разницу в голове.
| Принцип SOLID | Формулировка | Пример в Java |
|---|---|---|
| S — Single Responsibility | У класса одна причина для изменения | Класс либо считает зарплату, либо печатает отчёт, но не оба сразу |
| O — Open/Closed | Открыт для расширения, закрыт для изменения | Новый способ оплаты — новая реализация Payment, старый код не трогаем |
| L — Liskov Substitution | Потомок подставляется вместо родителя без сюрпризов | Manager работает везде, где ожидается Employee |
| I — Interface Segregation | Много узких интерфейсов лучше одного широкого | Readable и Writable вместо одного Storage |
| D — Dependency Inversion | Зависим от абстракций, а не от реализаций | Поле типа Payment, а не CardPayment |
Где чаще всего ошибаются
- Считают инкапсуляцию синонимом сокрытия данных. Сокрытие — только часть: инкапсуляция ещё и объединяет состояние с поведением в одном классе.
- Наследуют ради переиспользования кода. Если между классами нет отношения «является», получается хрупкая иерархия; композиция здесь надёжнее.
- Путают перегрузку и переопределение. Перегрузка — одинаковое имя и разные параметры в одном классе; переопределение — одинаковая сигнатура в потомке.
- Забывают
@Override. Аннотация не обязательна, но без неё опечатка в имени метода превращает переопределение в новый метод — и компилятор промолчит. - Называют композицию и агрегацию принципами ООП. Это виды отношений между объектами, а не принципы; на собеседовании такой ответ засчитают как ошибку.
- Утверждают, что Java — полностью объектно-ориентированный язык. Примитивные типы (
int,double,boolean) объектами не являются, поэтому Java считают гибридным языком.
Официальное введение в объекты и классы — раздел Object-Oriented Programming Concepts в Java Tutorials от Oracle.
Часто задаваемые вопросы
Сколько принципов ООП — три или четыре?
Классическая триада — инкапсуляция, наследование и полиморфизм. Многие авторы и большинство собеседований добавляют четвёртый принцип, абстракцию. Безопасный ответ: назвать все четыре и уточнить, что абстракцию иногда рассматривают как часть инкапсуляции.
Чем полиморфизм отличается от наследования?
Наследование — это способ построить новый класс на базе существующего, то есть отношение между классами. Полиморфизм — способность вызывать один и тот же метод у объектов разных классов и получать разное поведение. Наследование часто служит механизмом для полиморфизма, но полиморфизм достижим и через интерфейсы без общего родительского класса.
Композиция и агрегация — это принципы ООП?
Нет. Это виды отношений между объектами: агрегация означает, что объект содержит другой объект, который может существовать самостоятельно, а композиция — что вложенный объект без владельца не имеет смысла и уничтожается вместе с ним. Принципами ООП называют инкапсуляцию, наследование, полиморфизм и абстракцию.
Является ли Java полностью объектно-ориентированным языком?
Нет, Java считают гибридным языком. Весь код располагается в классах, но примитивные типы int, double, char и boolean объектами не являются, а статические методы вызываются без создания объекта. Полностью объектными считают, например, Smalltalk и Ruby.
Чем принципы ООП отличаются от SOLID?
Принципы ООП описывают устройство объектной модели языка: инкапсуляция, наследование, полиморфизм, абстракция. SOLID — это пять рекомендаций по проектированию классов, которые помогают писать поддерживаемый код поверх ООП. ООП отвечает на вопрос из чего состоит модель, SOLID — как её выстроить, чтобы она не разваливалась при изменениях.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии