ООП в Java
С появлением программирования на него сразу возложили задачи описания окружающего мира и моделирования происходящих процессов. Даже самые простые решения с самого начала составляли сотни строк сложного машинного кода, поэтому для упрощения работы придумали языки высокого уровня: FORTRAN, Algol и другие. В них программист уже мог писать человекочитаемый код, который потом переводился в машинный с помощью специальных программ-компиляторов (компилировался). Это сделало программы гораздо понятней и короче, и позволило с легкостью их усовершенствовать, т.к. над одной и той же задачей уже могли работать группы разработчиков, а человекочитаемый код — залог того, что все друг друга правильно понимают.
С последующим развитием программирования задачи усложнялись, что требовало нового и более комплексного подхода. Так и рождались парадигмы программирования, которые были в состоянии решать более комплексные, сложные и громоздкие задачи. Мы не будем детально рассматривать каждую парадигму, потому как это не урок истории, просто перечислим их:
- императивное программирование;
- декларативное программирование;
- структурное программирование;
- функциональное программирование;
- логическое программирование;
- объектно-ориентированное программирование (ООП).
Перейдем сразу к ООП — парадигме с которой мы будем работать ближайшее время. В какой-то момент программисты поняли, что программы удобно разбивать на совокупность взаимодействующих объектов, построенных по определенным классам. По аналогии из реального мира класс — это схема/план/чертеж/описание чего-то. Любой объект можно описать с помощью программы: это может быть как реальный физический объект (машина, человек, финансовое учреждение и т.д.), так и не физический объект (перевод в банке, природное явление, песня). Т.е. в виде объекта в программе можно описать и представить что угодно. Именно эта парадигма программирования используется в Java. Это современная, но НЕ ЕДИНСТВЕННАЯ парадигма программирования на текущий момент. К примеру, все большей популярности с каждым годом набирает функциональный подход. В то же время, в Java использовать подход ООП очень эффективно.
Рассмотрим вкратце объектно-ориентированное программирование, основанное на классах.
В объектно-ориентированном программировании предполагается, что любой предмет и явление можно описать в виде объекта. Каждый объект имеет свою план-схему по которой он строится. В програмировании такой схемой является класс. То есть класс — это кусок программы, который описывает какой-либо тип данных из реального мира. Абсолютно весь Java код должен быть написан внутри классов. Классы в Java располагаются в отдельных файлах и ДО этапа компиляции имеют разширение .java . На основании этих файлов компилятор создает новые — с разширением .class . Название класса принято писать с большой буквы. Также каждый класс чаще всего описывает какие-то поля — это свойства, которые будет содержать объект, созданный на основании данного класса. Они могут быть как числами, так и строками (текстом), либо иметь какой-то другой тип данных.
Рассмотрим класс, который описывает обычный дом (данный код размещается в файле House.java ):
public class House < //ниже указаны поля класса - свойства, которыми описывается данный объект int width; //ширина дома (целочисленное) int length; //длина дома (целочисленное) int floors; //количество этажей (целочисленное) String color; //цвет дома (строка) String address; //адрес, по которому находится дом (строка) >
Как видим, класс House описывает какой-то абстрактный дом, который имеет ширину, длину и количество этажей. Это целочисленные значения (тип int). Также дом покрашен в какой-то цвет и имеет свой адрес — это переменные строкового типа (тип переменной String). Поля класса еще называют свойствами класса или переменными класса; это все разные названия одного и того же понятия. Если Вы не понимаете каких-то нюансов, например, тип переменной целочисленное/строка — не переживайте, дальше по программе мы будем останавливаться на этих понятиях. Сейчас мы только с ними знакомимся.
Зачем же нам нужно планировать и описывать класс? Ответ на этот вопрос прост: на основании одного класса мы можем создавать сколько угодно объектов (экземпляров этого класса). То есть другими словами класс — это ничто иное, как план чего-то, а по этому плану можно создавать реальные объекты как в реальном мире (есть один план дома, и по нему строится сколько угодно домов). Создаются новые объекты (экземпляры класса) с помощью ключевого слова new , как показано в примере ниже:
House myHouse = new House(); // мы создали новый дом под переменной myHouse House parentHouse = new House(); // мы создали новый дом под переменной parentHouse House neighborHouse = new House(); // мы создали новый дом под переменной neighborHouse
Как видим, мы сначала описали класс House, который является по сути моделью обычного человеческого дома. А потом создали 3 экземпляра дома: myHouse, parentHouse, neighborHouse. Все три экземпляра являются абсолютно разными объектами, но основанными на одном классе.
Стоит заметить, что ООП имеет свои внутренние парадигмы и нюансы, с которыми мы детально ознакомимся дальше. А пока что мы узнали 2 вещи:
- любой физический предмет и нефизическое явление можно описать с помощью класса;
- на базе одного класса можно построить сколько угодно объектов.
Язык объектно ориентированного программирования java
Концепции ООП являются основополагающими элементами и составляют основу языка программирования Java. В рамках данного подхода выделяют следующие термины: абстракция, инкапсуляция, наследование и полиморфизм. Понимание данных принципов служит ключом к построению целостной картины того, как работают программы, написанные на Java. По большому счету, объектно-ориентированный подход позволяет нам описывать классы, определять методы и переменные таким образом, чтобы затем использовать их вновь, частично либо полностью, без нарушения безопасности.
- Абстракция. Абстракция означает использование простых вещей для описания чего-то сложного. Например, мы все знаем как пользоваться телевизором, но в тоже время нам не нужно обладать знаниями о том, как он работает чтобы смотреть его. В Java под абстракцией подразумеваются такие вещи, как объекты, классы и переменные, которые в свою очередь лежат в основе более сложного кода. Использование данного принципа позволяет избежать сложности при разработке ПО.
- Инкапсуляция. Под инкапсуляцией подразумевается сокрытие полей внутри объекта с целью защиты данных от внешнего, бесконтрольного изменения со стороны других объектов. Доступ к данным (полям) предоставляется посредством публичных методов (геттеров/сеттеров). Это защитный барьер позволяет хранить информацию в безопасности внутри объекта.
- Наследование. Это особая функциональность в объектно-ориентированных языках программирования, которая позволяет описывать новые классы на основе уже существующих. При этом поля и методы класса-предка становятся доступны и классам-наследникам. Данная фича делает классы более чистыми и понятным за счет устранения дублирования программного кода.
- Полиморфизм. Данный принцип позволяет программистам использовать одни и те же термины для описания различного поведения, зависящего от контекста. Одной из форм полиморфизма в Java является переопределение метода, когда различные формы поведения определяются объектом из которого данный метод был вызван. Другой формой полиморфизма является перегрузка метода, когда его поведение определяется набором передаваемых в метод аргументов.
Концепции ООП в Java позволяют программистам создавать компоненты, которые можно переиспользовать в различных частях программы не подвергая данные опасности.
Основная цель использования данной концепции — это уменьшение сложности компонентов программы за счет скрытия от программиста, использующего эти компоненты, ненужных ему подробностей. Это позволяет реализовать более сложную логику поверх предоставленной абстракции, не вдаваясь в подробности ее реализации.
Приготовление кофе с помощью кофемашины является хорошим примером абстракции. Все, что нам надо знать, что бы ей пользоваться: как налить воды, засыпать кофейные зерна, включить и выбрать вид кофе, который хотим получить. А, как машина будет варить кофе — нам знать не нужно.
В данном примере кофемашина представляет собой абстракцию, которая от нас скрывает все подробности варки кофе. Нам лишь остается просто взаимодействовать с простым интерфейсом, который не требует от нас каких-либо знаний о внутренней реализации машины.
Этот же подход можно использовать и в объектно-ориентированных языках программирования, таких как Java.
Инкапсуляция позволяет нам пользоваться возможностями класса без создания угрозы безопасности данных за счет ограничения прямого доступа к его полям. Также она позволяет изменять код классов не создавая проблем их пользователям (другим классам). В Java данный принцип достигается за счет использования ключевого слова private .
Наследование — еще одна важная концепция ООП, которая позволяет сэкономить время на написании кода. Возможности наследования раскрываются в том, что новому классу передаются свойства и методы уже описанного ранее класса. Класс, который наследуется называется дочерним (или подклассом). Класс, от которого наследуется новый класс — называется родительским, предком и т. д. В языке программирования Java используется ключевое слово extends для того, чтобы указать на класс-предок.
Полиморфизм предоставляет возможность единообразно обрабатывать объекты с различной реализацией при условии наличия у них общего интерфейса или класса. По-простому: способность вызывать нужные методы у объектов, имеющие разные типы (но находящиеся в одной иерархии). При этом происходит автоматический выбор нужного метода в зависимости от типа объекта.
Рассмотрим примеры полиморфизма в Java: переопределение и перегрузка методов.
В случае с переопределением метода, дочерний класс, используя концепцию полиморфизма, может изменить (переопределить) поведение метода родительского класса. Это позволяет программисту по разному использовать один и тот же метод, определяя поведение из контекста вызова (вызывается метод из класса предка или класса наследника).
В случае же с перегрузкой, метод может проявлять различное поведение в зависимости от того, какие аргументы он принимает. В данном случае контекст вызова определяется набором параметров метода.