Ниже представлена подробная восьмая глава, ориентированная на новичков. В предыдущих главах мы познакомились с основами языка, научились работать с типами данных, писать методы, изучили строки, массивы и коллекции. Теперь пришло время сделать большой шаг вперёд и познакомиться с объектно-ориентированным программированием (ООП) — парадигмой, лежащей в основе Java.
Объектно-ориентированное программирование (ООП) — это подход к разработке программ, в котором всё рассматривается как объекты, а код строится вокруг их взаимодействия. Программы, написанные в процедурном стиле, представляют собой набор инструкций и данных, а ООП помогает объединять данные и действия над этими данными в логические единицы — объекты.
Почему ООП удобно?
Класс — это шаблон или чертёж, по которому создаются объекты. Он определяет свойства (поля) и действия (методы), которые будут у объектов этого типа.
Объект — это конкретный экземпляр класса. Если класс — это "чертёж дома", то объект — это конкретный построенный дом по этому чертежу.
Пример:
Создадим класс 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 лет.
Один из принципов ООП — инкапсуляция. Он предполагает скрытие внутренних деталей объекта от внешнего мира. Вы открываете доступ только к тому, что нужно.
Для этого используют модификаторы доступа: public
, private
, protected
.
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 + " лет.");
}
}
Теперь мы можем быть уверены, что возраст не будет установлен отрицательным. Внешний код взаимодействует с объектом через геттеры и сеттеры, не имея прямого доступа к внутренним полям.
Конструктор — это специальный метод, который вызывается при создании объекта класса. Он обычно инициализирует поля.
Если вы не написали конструктор, компилятор сгенерирует конструктор по умолчанию (без параметров).
Пример:
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 лет.
Наследование — это способность одного класса "наследовать" поля и методы другого. Это позволяет использовать общий код. Например, есть класс 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 лет.
Олег учится в МГУ
Полиморфизм означает способность объектов вести себя по-разному в зависимости от того, к какому классу они относятся. Он часто достигается через переопределение методов.
Представьте, что у нас есть метод 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 лет, я учусь в МГУ
ООП также включает понятия абстракции: создание абстрактных классов и интерфейсов. Это более продвинутые темы, но кратко:
Абстрактный класс: класс, который не может быть создан напрямую (нет объектов), но служит как "шаблон". В нём можно объявлять абстрактные методы без реализации, а уже конкретные подклассы реализуют эти методы.
Интерфейс: это "контракт", который говорит: "Класс, который реализует этот интерфейс, обязан предоставить реализацию таких-то методов." Например, интерфейс Comparable
требует реализации метода compareTo()
, чтобы можно было сравнивать объекты.
Эти инструменты делают код ещё более гибким и расширяемым.
Представим, что мы делаем простую программу "Люди и студенты". Мы создадим класс 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
мы можем добавлять людей разных типов, и они будут вести себя согласно своему классу.
В этой главе мы познакомились с основами ООП:
Это фундаментальные концепции, на которых строятся сложные системы. Понимание ООП позволит вам создавать чистый, понятный и расширяемый код.
На этом мы завершаем восьмую главу. В следующей главе мы поговорим о том, как обрабатывать ошибки и исключительные ситуации в программе с помощью исключений. Это поможет сделать ваш код более надёжным и готовым к непредвиденным обстоятельствам.
Просмотров: 62