В прошлой лекции было сказано, что Java — объектно-ориентированный язык программирования. Поясним, что это значит. Мы взглянем на понятие объектно-ориентированное программирование с 2 различных точек зрения.

Первая точка зрения на ООП

Рассмотрим (в сильно утрированном виде, разумеется) эволюцию языков программирования, от самых старых до современных.

  1. Машинный код: Программирование ведется непосредственно в командах процессора, на котором будет выполняться программа. Каждая команда — это последовательность двоичных чисел. Очевидно, что такой способ программирования чрезвычайно сложен и написать сколько-нибудь большую программу практически невозможно.
  2. Ассемблер: Отличается от машинного кода тем, что кодам машинных команд сопоставлены слова, например add, mov, что, очевидно, сильно повышает читаемость программы. Это — уже язык программирования, хоть и примитивный. Однако и на этом языке крайне сложно программировать достаточно большие проекты.
  3. «Языки высокого уровня»: Появляются такие идеи, как именованные переменные и подпрограммы, управляющие конструкции (if, while…) Развитие идет в сторону повышения структурированности кода (появления дополнительных конструкций) и повышения модульности программы. Программы становятся более понимаемыми и читабельными.
  4. «Языки со структурированием данных»: Языки программирования предоставляют программисту возможность описывать свои собственные составные типы данных (записи, структуры и т.п.). Это позволяет объединять вместе данные со схожим назначением и видеть их как одно целое, например можно объединить в структуре «Прямоугольник» 4 его координаты вместо того, чтобы заводить 4 переменных.
    Идея структурирования данных возникла и была реализована позже идей, описанных в п.3.
  5. Объектно-ориентированное программирование: При дальнейшем усложнении программ начало не хватать только лишь структурирования кода и структурирования данных: появилась необходимость в объединении подпрограмм в блоки. Эту задачу решает не только ООП — в частности, шагом в эту сторону является и появление библиотек подпрограмм — например, 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 попытается:

  1. Найти в текущем каталоге файл HelloWorld.class .
  2. Найти в содержащемся там классе HelloWorld функцию main, объявленную именно таким образом.
  3. Выполнить ее.

Если указать аргументы командной строки, то они будут переданы в массив 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!».