Наследование, инкапсуляция и полиморфизм — это три ключевых принципа объектно-ориентированного программирования (ООП), которые помогают создавать гибкие, повторно используемые и легко поддерживаемые программы. Давайте рассмотрим их более подробно.
1. Наследование (Inheritance)
Наследование позволяет создавать новый класс на основе уже существующего. Новый класс наследует свойства и методы родительского класса, а также может добавлять свои собственные свойства и методы или изменять существующие.
Пример:
// Родительский класс (superclass)
class Animal {
String name;
// Метод родительского класса
public void speak() {
System.out.println(name + " издает звук");
}
}
// Дочерний класс (subclass) наследует Animal
class Dog extends Animal {
// Метод дочернего класса
public void speak() {
System.out.println(name + " лает");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Animal();
animal.name = "Животное";
animal.speak(); // Выведет: Животное издает звук
Dog dog = new Dog();
dog.name = "Собака";
dog.speak(); // Выведет: Собака лает
}
}
В этом примере:
- Класс Dog
наследует класс Animal
.
- Класс Dog
переопределяет метод speak()
, что называется переопределением методов (method overriding).
- Дочерний класс может также добавлять новые методы или поля, не изменяя родительский класс.
2. Инкапсуляция (Encapsulation)
Инкапсуляция — это принцип скрытия внутренних деталей реализации объекта и предоставления доступа к данным только через специально определенные методы. Это позволяет контролировать доступ к данным и обеспечивать их целостность.
Пример:
class BankAccount {
private double balance; // Приватное поле (нельзя доступить напрямую из других классов)
// Публичный метод для получения баланса
public double getBalance() {
return balance;
}
// Публичный метод для внесения средств
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
// Публичный метод для снятия средств
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount();
account.deposit(1000); // Вносим деньги
account.withdraw(500); // Снимаем деньги
System.out.println("Баланс: " + account.getBalance()); // Выведет: Баланс: 500.0
}
}
В этом примере:
- Поле balance
объявлено как private
, что означает, что оно недоступно извне класса.
- Для работы с полем balance
создаются публичные методы (getBalance()
, deposit()
, withdraw()
), которые управляют доступом к этому полю.
- Это позволяет контролировать, как изменяются данные (например, проверка на снятие средств только при наличии достаточного баланса).
3. Полиморфизм (Polymorphism)
Полиморфизм позволяет объектам разных классов обрабатывать одно и то же сообщение (вызов метода) по-разному. Это позволяет использовать методы и интерфейсы с одинаковыми именами для разных типов данных. Полиморфизм бывает двух типов:
- Компиляционный полиморфизм (или статический полиморфизм): это перегрузка методов.
- Полиморфизм времени выполнения (или динамический полиморфизм): это переопределение методов.
Пример полиморфизма (динамический):
// Родительский класс
class Animal {
public void speak() {
System.out.println("Животное издает звук");
}
}
// Дочерний класс
class Dog extends Animal {
public void speak() {
System.out.println("Собака лает");
}
}
// Дочерний класс
class Cat extends Animal {
public void speak() {
System.out.println("Кошка мяукает");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal = new Animal(); // Создаем объект родительского класса
Animal myDog = new Dog(); // Создаем объект дочернего класса
Animal myCat = new Cat(); // Создаем объект другого дочернего класса
myAnimal.speak(); // Выведет: Животное издает звук
myDog.speak(); // Выведет: Собака лает
myCat.speak(); // Выведет: Кошка мяукает
}
}
В этом примере:
- Метод speak()
переопределяется в каждом дочернем классе.
- Несмотря на то, что все объекты имеют тип Animal
, каждый из них вызывает свой собственный метод speak()
в зависимости от реального типа объекта (это и есть полиморфизм).
Пример полиморфизма (перегрузка):
class Printer {
// Перегрузка метода print
public void print(String message) {
System.out.println("Печать сообщения: " + message);
}
public void print(int number) {
System.out.println("Печать числа: " + number);
}
public void print(double value) {
System.out.println("Печать числа с плавающей точкой: " + value);
}
}
public class Main {
public static void main(String[] args) {
Printer printer = new Printer();
printer.print("Привет!"); // Выведет: Печать сообщения: Привет!
printer.print(100); // Выведет: Печать числа: 100
printer.print(3.14); // Выведет: Печать числа с плавающей точкой: 3.14
}
}
Здесь:
- Мы перегружаем метод print
в одном классе, чтобы он принимал различные типы параметров.
- Это пример компиляционного полиморфизма (перегрузки методов).
Резюме
- Наследование позволяет создавать новый класс на основе уже существующего, переопределяя или расширяя его функциональность.
- Инкапсуляция скрывает внутреннюю реализацию класса и предоставляет доступ к данным через методы (getter и setter), что повышает безопасность данных.
- Полиморфизм позволяет использовать один и тот же интерфейс для объектов разных типов, а также позволяет перегружать и переопределять методы для достижения гибкости в коде.
Эти принципы вместе позволяют создавать мощные, гибкие и легко поддерживаемые программы.