Первая точка зрения на ООП
Рассмотрим (в сильно утрированном виде, разумеется) эволюцию языков программирования, от самых старых до современных.
- Машинный код: Программирование ведется непосредственно в командах процессора, на котором будет выполняться программа. Каждая команда — это последовательность двоичных чисел. Очевидно, что такой способ программирования чрезвычайно сложен и написать сколько-нибудь большую программу практически невозможно.
- Ассемблер: Отличается от машинного кода тем, что кодам машинных команд сопоставлены слова,
например
add
,mov
, что, очевидно, сильно повышает читаемость программы. Это — уже язык программирования, хоть и примитивный. Однако и на этом языке крайне сложно программировать достаточно большие проекты. - «Языки высокого уровня»: Появляются такие идеи, как именованные переменные и подпрограммы, управляющие конструкции (if, while ) Развитие идет в сторону повышения структурированности кода (появления дополнительных конструкций) и повышения модульности программы. Программы становятся более понимаемыми и читабельными.
- «Языки со структурированием данных»: Языки программирования предоставляют программисту возможность
описывать свои собственные составные типы данных (записи, структуры и т.п.). Это позволяет объединять вместе
данные со схожим назначением и видеть их как одно целое, например можно объединить в структуре «Прямоугольник»
4 его координаты вместо того, чтобы заводить 4 переменных.
Идея структурирования данных возникла и была реализована позже идей, описанных в п.3. - Объектно-ориентированное программирование: При дальнейшем усложнении программ начало не хватать только лишь структурирования кода и структурирования данных: появилась необходимость в объединении подпрограмм в блоки. Эту задачу решает не только ООП — в частности, шагом в эту сторону является и появление библиотек подпрограмм — например, unit'ов в Pascal. ООП — один из возможных подходов.
Возникает вопрос: каким образом группировать подпрограммы в блоки? Объектно-ориентированный подход состоит в том, что каждая подпрограмма работает с определенным набором данных, и с каждым набором данных оперирует определенный набор подпрограмм. Отсюда возникает идея: объединить подпрограммы по типам обрабатываемых данных. Другими словами, объединить данные и подпрограммы для их обработки.
Например, рассмотрим сущность «Дом». Для его описания используются какие-то данные, например количество этажей, цвет и т. п., и он обладает такими функциями как «нарисовать себя», «изменить цвет» и т. п. Сущность, в которой объединены данные и методы их обработки, называется объектом.
Если на С++, несмотря на его возможности объектно-ориентированного программирования, можно писать программы, не использующие объекты, то на Java это невозможно. В Java любая сущность является объектом, и любая функция принадлежит какому-либо типу.
Приведем простейшую программу на Java:
public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, world!"); } }
В этой программе есть 1 класс (то же, что тип — в дальнейшем будет употребляться слово «класс») под названием
HelloWorld
, а в этом классе есть одна функция — main
.
По правилам языка Java запустить можно лишь такую программу, у которой в одном из классов определена именно таким образом функция main
с именно такими аргументами.
В теле этой функции написана инструкция, выводящая на экран сообщение «Hello world!». Не углубляясь пока в подробности, можно сказать, что System.out.println
— это аналог
writeln
в Pascal.
String[] args
в параметрах функции main
означает, что ей передается массив строк
под именем args
.
Для того, чтобы можно было скомпилировать и запустить эту программу, ее исходный текст должен находиться в файле
HelloWorld.java
(в каждом файле содержится ровно один класс, который обязан называться
так же, как и файл).
Для компиляции необходимо выполнить команду
javac HelloWorld.java
В результате ее выполнения, если
компиляция прошла без ошибок, создастся файл HelloWorld.class
, в котором находится
байт-код соответствующего класса.
Чтобы запустить программу, надо выполнить команду
java HelloWorld [аргументы]
При этом среда выполнения Java попытается:
- Найти в текущем каталоге файл
HelloWorld.class
. - Найти в содержащемся там классе
HelloWorld
функциюmain
, объявленную именно таким образом. - Выполнить ее.
Если указать аргументы командной строки, то они будут переданы в массив args
в аргументах функции main
.
Эта программа — не объектно-ориентированная, хоть и написана на Java. В самом деле, в ней не создается ни одного объекта.
Вторая точка зрения на ООП
В процедурно-ориентированных языках, таких как С или Pascal, для каждого действия есть отдельная процедура. Если попробовать описать русским языком работу программы на процедурно-ориентированном языке, то получится текст с одним подлежащим «Программа» и множеством сказуемых: «Программа делает то-то ». Каждому действию соответствует глагол с дополнением — аргументами.
С этой точки зрения выполнение объектно-ориентированной программы описывается текстом, в котором есть и подлежащие, и сказуемые: «Дом рисуется» и т. п.
В ООП «главными» считаются не действия программы, а объекты, которые их совершают. Тезисно это можно сформулировать и так: Если какое-то действие совершается — значит, кто-то его совершает.
Попробуем применить это к нашей программе. С не-объектно-ориентированной точки зрения непонятно, какой смысл применять к ней эту концепцию, и, вообще говоря, непонятно, кто совершает действие по выводу на экран «Hello World!». Однако, хоть это и выглядит искусственно (эта программа — не самый удачный пример демонстрации концепций ООП), такой класс/объект можно придумать. Назовем его HelloWriter (действительно: того, кто пишет «Hello» логично назвать «Писатель Hello»).
Модифицированная программа выглядит так:
HelloWriter.java: public class HelloWriter { public void doIt() { System.out.println("Hello world!"); } } Main.java: public class Main { public static void main(String[] args) { HelloWriter writer = new HelloWriter(); writer.doIt(); } }
Компиляция и запуск:
javac HelloWriter.java Main.java java Main
Эта программа выглядит существенно сложнее предыдущей, но она гораздо более «объектно-ориентированная».
Рассмотрим новые элементы в ней.
HelloWriter writer = new HelloWriter();
— эта строчка создает новый экземпляр
класса HelloWriter и инициализирует ссылкой на него переменную writer (здесь «=» — не присваивание, а инициализация).
Важно понимать, что в переменной writer записан не сам объект, а лишь ссылка на область памяти, в которой находится
объект. Поэтому, если бы в программе было две переменных HelloWriter writer1, writer2;
то присваивание writer1 = writer2;
привело бы не к копированию объекта, на который
ссылается writer2, в объект, на который ссылается writer1, а лишь к тому, что переменные writer1 и writer2
стали бы указывать на одно и то же место.
writer.doIt();
— эта строка вызывает у объекта writer метод doIt()
, который, собственно, и выполняет работу по выводу на экран сообщения «Hello world!».