Глава 7. Массивы и коллекции ← Вернуться к списку глав Глава 9. Обработка ошибок и исключений

Глава 8. Объектно-ориентированное программирование (ООП)

Ниже представлена подробная восьмая глава, ориентированная на новичков. В предыдущих главах мы познакомились с основами языка, научились работать с типами данных, писать методы, изучили строки, массивы и коллекции. Теперь пришло время сделать большой шаг вперёд и познакомиться с объектно-ориентированным программированием (ООП) — парадигмой, лежащей в основе Java.


8.1 Что такое ООП и зачем оно нужно?

Объектно-ориентированное программирование (ООП) — это подход к разработке программ, в котором всё рассматривается как объекты, а код строится вокруг их взаимодействия. Программы, написанные в процедурном стиле, представляют собой набор инструкций и данных, а ООП помогает объединять данные и действия над этими данными в логические единицы — объекты.

Почему ООП удобно?

8.2 Классы и объекты

Класс — это шаблон или чертёж, по которому создаются объекты. Он определяет свойства (поля) и действия (методы), которые будут у объектов этого типа.

Объект — это конкретный экземпляр класса. Если класс — это "чертёж дома", то объект — это конкретный построенный дом по этому чертежу.

Пример:
Создадим класс Person (Человек), у которого есть имя и возраст, и который умеет "представляться".

public class Person {
    String name; // поле (свойство)
    int age;     // поле (свойство)

    // метод (действие)
    void introduce() {
        System.out.println("Привет! Меня зовут " + name + ", мне " + age + " лет.");
    }
}

Этот класс — только шаблон. Чтобы им воспользоваться, нужно создать объект:

public class Main {
    public static void main(String[] args) {
        Person p = new Person(); // создание объекта класса Person
        p.name = "Иван";
        p.age = 25;
        p.introduce(); // вызов метода
    }
}

Вывод:

Привет! Меня зовут Иван, мне 25 лет.

8.3 Инкапсуляция

Один из принципов ООП — инкапсуляция. Он предполагает скрытие внутренних деталей объекта от внешнего мира. Вы открываете доступ только к тому, что нужно.

Для этого используют модификаторы доступа: public, private, protected.

Часто поля делают private, а для их чтения и изменения пишут специальные методы — геттеры и сеттеры.

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) { // конструктор
        this.name = name;
        this.age = age;
    }

    public String getName() { // геттер для name
        return name;
    }

    public int getAge() { // геттер для age
        return age;
    }

    public void setAge(int age) { // сеттер для age
        if (age > 0) { // логика проверки
            this.age = age;
        }
    }

    public void introduce() {
        System.out.println("Привет! Меня зовут " + name + ", мне " + age + " лет.");
    }
}

Теперь мы можем быть уверены, что возраст не будет установлен отрицательным. Внешний код взаимодействует с объектом через геттеры и сеттеры, не имея прямого доступа к внутренним полям.

8.4 Конструкторы

Конструктор — это специальный метод, который вызывается при создании объекта класса. Он обычно инициализирует поля.

Если вы не написали конструктор, компилятор сгенерирует конструктор по умолчанию (без параметров).

Пример:

public class Person {
    private String name;
    private int age;

    // Конструктор
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // методы ...
}

Теперь при создании объекта:

Person p = new Person("Анна", 30);
p.introduce();

Вывод:

Привет! Меня зовут Анна, мне 30 лет.

8.5 Наследование

Наследование — это способность одного класса "наследовать" поля и методы другого. Это позволяет использовать общий код. Например, есть класс Person, а есть класс Student, который также человек, но со своими дополнительными свойствами.

public class Student extends Person {
    private String university;

    public Student(String name, int age, String university) {
        super(name, age); // вызов конструктора родителя Person
        this.university = university;
    }

    public void study() {
        System.out.println(getName() + " учится в " + university);
    }
}

Теперь Student имеет всё, что есть у Person (имя, возраст, метод introduce()), плюс свои поля и методы.

Student s = new Student("Олег", 20, "МГУ");
s.introduce(); // метод от Person
s.study();     // свой метод

Вывод:

Привет! Меня зовут Олег, мне 20 лет.
Олег учится в МГУ

8.6 Полиморфизм

Полиморфизм означает способность объектов вести себя по-разному в зависимости от того, к какому классу они относятся. Он часто достигается через переопределение методов.

Представьте, что у нас есть метод introduce() у Person. Если Student переопределит этот метод, он будет работать чуть иначе.

public class Student extends Person {
    private String university;

    public Student(String name, int age, String university) {
        super(name, age);
        this.university = university;
    }

    @Override
    public void introduce() {
        // Переопределение метода
        System.out.println("Привет! Я студент " + getName() 
            + ", мне " + getAge() + " лет, я учусь в " + university);
    }
}

Теперь, если вызвать introduce() для Person и Student, результаты будут разными.

Person p = new Person("Иван", 30);
Student s = new Student("Олег", 20, "МГУ");

p.introduce();
s.introduce();

Вывод:

Привет! Меня зовут Иван, мне 30 лет.
Привет! Я студент Олег, мне 20 лет, я учусь в МГУ

8.7 Абстракция и интерфейсы

ООП также включает понятия абстракции: создание абстрактных классов и интерфейсов. Это более продвинутые темы, но кратко:

Эти инструменты делают код ещё более гибким и расширяемым.

8.8 Пример использования ООП в маленьком проекте

Представим, что мы делаем простую программу "Люди и студенты". Мы создадим класс Person, класс Student, а затем в main создадим список людей, часть из которых — студенты, и выведем их описание.

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void introduce() {
        System.out.println("Привет! Меня зовут " + name + ", мне " + age + " лет.");
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
public class Student extends Person {
    private String university;

    public Student(String name, int age, String university) {
        super(name, age);
        this.university = university;
    }

    @Override
    public void introduce() {
        System.out.println("Привет! Я студент " + getName() + ", мне " + getAge() 
            + " лет, я учусь в " + university);
    }
}
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Person> people = new ArrayList<>();
        people.add(new Person("Иван", 30));
        people.add(new Student("Олег", 20, "МГУ"));
        people.add(new Person("Анна", 25));
        people.add(new Student("Мария", 19, "СПбГУ"));

        for (Person p : people) {
            p.introduce(); // метод работает по-разному, в зависимости от конкретного типа объекта
        }
    }
}

Результат:

Привет! Меня зовут Иван, мне 30 лет.
Привет! Я студент Олег, мне 20 лет, я учусь в МГУ
Привет! Меня зовут Анна, мне 25 лет.
Привет! Я студент Мария, мне 19 лет, я учусь в СПбГУ

Таким образом, без изменения кода main мы можем добавлять людей разных типов, и они будут вести себя согласно своему классу.

8.9 Подводим итоги

В этой главе мы познакомились с основами ООП:

Это фундаментальные концепции, на которых строятся сложные системы. Понимание ООП позволит вам создавать чистый, понятный и расширяемый код.


На этом мы завершаем восьмую главу. В следующей главе мы поговорим о том, как обрабатывать ошибки и исключительные ситуации в программе с помощью исключений. Это поможет сделать ваш код более надёжным и готовым к непредвиденным обстоятельствам.

Глава 7. Массивы и коллекции ← Вернуться к списку глав Глава 9. Обработка ошибок и исключений

Просмотров: 62