Четыре принципа объектно-ориентированного программирования в Java


Оглавление (нажмите, чтобы открыть):

Объектно-ориентированное программирование в Java

Объектно-ориентированное программирование в Java

Статья написана на основе официальной документации фирмы Sun Microsystems, Inc

Что такое объект?

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

Объекты реального мира наделены двумя характеристиками: состояние и поведение. Собака имеет «состояние» (кличка, масть, шерсть, аппетит) и поведение (лай, виляние хвостом). Велосипед тоже имеет состояние (текущая передача, текущая скорость) и поведение (изменение передачи, изменение скорости). Чтобы начинать думать в терминах объектно-ориентированного программирования, надо выделить состояние и поведение объекта. Прямо сейчас уделите пару минут рассмотрению объектов реального мира, которые располагаются вокруг Вас. Для каждого объекта, которые Вы видите, ответьте на вопросы: «Какие возможные состояния может принимать этот объект?», «Какие действия может выполнять этот объект?». Запишите Ваши наблюдения. Вы, наверняка, заметили, что объекты различны по сложности; Ваша настольная лампа имеет два возможных состояния (включена и выключена) и два возможных действия (включить, выключить), однако настольное радио может иметь такой набор состояний: включено, выключено, текущая громкость, текущая станция; и возможные действия: включить, выключить, увеличить громкость, уменьшить громкость, искать, станцию и т.д. Кроме того, Вы можете заметить, что некоторые объекты содержат другие объекты. Все эти объекты реального мира могут быть переведены в объекты объектно-ориентированного программирования.

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

Через атрибуты состояния (текущая скорость, текущая передача) и методы изменения этого состояния объект сохраняет корректность своего состояния и показывает как может быть использован. Например, если велосипед имеет только шесть передач, метод изменения передачи не примет значения, которые меньше одного и больше шести.


    Сокрытие кода внутри объекта имеет ряд преимуществ:
  • Модульность. Исходный код объекта может быть написан и изменен независимо от кода других объектов. Однажды созданный, объект может быть произвольно использован в системе.
  • Сокрытие информации. Все взаимодействия с объектом осуществляются по средствам методов, при этом детали внутренней реализации скрыты от внешнего мира.
  • Повторное использование кода. Если объект уже существует (возможно, создан другим разработчиком), Вы можете использовать его в своей программе. При таком подходе, Вы можете использовать специфические объекты, написанные и протестированные узкими специалистами.
  • Легкость отладки и подключения. Если какой-то объект работает нестабильно, Вы можете просто удалить его из приложения и заменить другим. В качестве аналогии можно привести пример из реального мира. Если у Вас сломался болт, то Вы меняете сломанный болт, а ни весь механизм.

Что такое класс?

В реальном мире Вы часто встречаете множество объектов одного вида. Например, это могут быть тысячи велосипедов одного производства и одной модели. Все велосипеды собраны по одному набору чертежей и поэтому состоят из одних и тех же компонентов. В объектно-ориентированных терминах мы можем сказать, что Ваш велосипед — это экземпляр класса объектов, названных велосипеды. Класс — этот и есть тот самый чертеж, из которого создаются конкретные объекты.

Ниже приведен пример класса, одна из возможных реализаций.

Синтаксис языка программирования Java покажется вам новым, однако логика класса основана на предыдущем обсуждении объектов велосипед. Поля cadence, speed и gear — это состояние объекта, а методы (changeCadence, changeGear, speedUp и т.д.) определяют взаимодействие с внешним миром.

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

Ниже представлен пример класса BicycleDemo, класс создает два отдельных объекта Велосипед и вызывает их методы.

Вот вывод текущих состояний объектов:
cadence:50 speed:10 gear:2
cadence:40 speed:20 gear:3

Что такое наследование?

Многие виды объектов часто содержат много общего. Например, горный велосипед, шоссейный велосипед, тандем, у всех этих объектов есть общие велосипедные свойства (текущая скорость, темп кручения педалей, текущая передача). Однако у этих видов велосипедов есть и отличия: тандем имеет два сидения, два руля; шоссейный велосипед имеет модифицированный руль, некоторые горные велосипеды имеют дополнительную цепную шестеренку пониженной передачи.

Объектно-ориентированное программирование позволяет классам наследовать часто используемые поведение и состояние другого класса. Например, Велосипед является суперклассом для классов ГорныйВелопед, ШоссейныйВелосипед и Тандем. В языке программирования Java каждый класс может иметь только один суперкласс, однако один суперкласс может иметь неограниченное число потомков.

Синтаксис создания класса потомка аналогичен типовому классу. Вначале объявления класса, используя ключевое слово extends, укажите имя класса, от которого надо наследовать.

Таким образом, ГорныйВелосипед имеет все поля и методы класса Велосипед, кроме того он содержит специфические особенности, делающие этот класс уникальным. Этот подход делает класс-потомок простым для понимания. Однако, Вам необходимо позаботиться о надлежащем документировании состояния и поведения суперкласса, т.к. этот код войдет в состав всех классов-потомков.

Что такое интерфейс?

Как Вы уже знаете, объект определяет свое взаимодействие с внешним миром через публичные методы. Методы — это интерфейс объекта с внешним миром. Например, кнопки на лицевой стороне Вашего телевизора — это интерфейс между Вами и электронными частями внутри пластиковой коробки. Вы нажимаете кнопку «Питание», чтобы включить или выключить телевизор.
В общем виде, интерфейс — это группа родственных методов с пустыми телами. Поведение велосипеда, определяемое как интерфейс, может быть представлено следующим образом:

Чтобы реализовать этот интерфейс, Вы должны создать свой класс с другим именем (например, ACMEBicycle), а в декларации класса надо использовать специальное ключевое слово.

Реализация интерфейса позволяет создавать более стандартизированные классы, с более предсказуемым поведением. Интерфейс — это контракт взаимодействия между классом и внешним миром, и этот контракт проверяется компилятором во время компиляции класса. Чтобы файл успешно скомпилировать, необходимо написать тела для всех методов, объявленных в интерфейсе.
Примечание: Чтобы скомпилировать класс ACMEBicycle, Вам надо добавить ключевое слово public в начало интерфейсных методов.

Что такое пакет?

Пакет — это пространство имен, в котором содержатся родственные классы и интерфейсы. Концептуально Вы можете думать о пакетах, как о различных папках у Вас на компьютере. Вы можете хранить страницы HTML в одной папке, картинки в другой, скрипты и приложения в третьей. Т.к. приложение на языке Java может состоять из сотен или тысяч отдельных классов, наличие способа упорядоченного хранения родственных классов и интерфейсов выглядит весьма привлекательно и логично.

Платформа Java предлагает огромную библиотеку классов (набор пакетов), готовых для использования в Ваших приложениях. Эта библиотека известна как Интерфейс Программирования Приложений («Application Programming Interface») или API. Эти пакеты выполняют наиболее часто встречающиеся задачи программирования. Например, объект String содержит состояние и поведение символьных строк; объект File позволяет программисту легко совершать операции (создавать, удалять, переименовывать и т.д.) с файлами операционной системы; объект Socket позволяет создавать и использовать сетевые сокеты; различные GUI (графический интерфейс пользователя) объекты: кнопки, chekbox’ы и т.д. могут быть использованы для создания графического интефейса. Вы можете выбирать буквально из тысяч классов. Это позволят разработчику сфокусироваться на создании логики приложения и не отвлекаться на технические детали реализации.
Java Platform API Specification (https://java.sun.com/javase/6/docs/api/index.html) содержит полный список всех пакетов, интерфейсов, классов, полей и методов, которые предоставляет Java Platform 6, Standard Edition. Трудно переоценить важность этой страницы для программиста, сделайте на нее закладку в Вашем браузере.

Внимание.
Комментировать могут только зарегистрированные пользователи.
Возможно использование следующих HTML тегов: , , ,
.

Объектно-ориентированное Java

Как отмечалось во вступлении, язык программирования Java является полностью объектно-ориентированным. Это означает, что для составления даже самой простой программы необходимо описать класс. Однако в языке программирования Java, кроме классов и объектов, есть, на что обратить внимание. Рассмотрение методов программирования в Java начнем с наиболее простых случаев. Некоторые базовые синтаксические конструкции как основу создания программы в Java, дадим более подробные объяснения по этому поводу, причем в контексте методов объектно-ориентированного программирования. Думается, такой подход, с одной стороны, позволит читателю, не знакомому с концепцией ООП, легче и быстрее усваивать новый материал, а затем плавно перейти к созданию реальных объектно-ориентированных программ в Java. С другой стороны, практически не пострадают те, кто знаком с методами ООП (например, программирующие на C++), поскольку представленный далее материал в любом случае важен для понимания принципов программирования в Java. Обычно программы пишут для того, чтобы обрабатывать данные. Методы и возможности по обработке данных в значительной степени зависят от типа данных. Язык Java относится к строго типизованным языкам. Это означает, что любая переменная в программе относится к определенному типу данных одному и только одному. В Java все данные можно разделить на: простые и ссылочные. Ссылочные данные реализуются через иерархию классов. Простые данные это скорее дань традиции. Забегая наперед, отметим, что для простых типов данных существуют ссылочные аналоги. Разница между простыми и ссылочными типами на практике проявляется при передаче аргументов методам. Простые типы данных передаются по значению, ссылочные через ссылку. И что простые типы данных являются, по сути, базовыми. В Java существует четыре группы базовых типов: для работы с целыми числами, для работы с числами в формате с плавающей точкой (действительные числа), символы и логический тип, таким образом, всего получается восемь базовых типов. Базовые типы Java перечислены в табл.1.1 В этой же таблице приведены названия классов-оболочек для базовых типов. Классы-оболочки используются в тех случаях, когда переменную соответствующего типа необходимо рассматривать как объект. В первую очередь стоит обратить внимание на целочисленные типы данных. В Java существует четыре типа целочисленных данных: byte, short, int и long. Отличаются типы количеством битов, выделяемых для записи значения соответствующего типа. Размер в битах увеличивается от 8 для типа byte до 32 для типа long (с шагом дискретности 8 бит). На практике выбор подходящего типа осуществляется в соответствии с предполагаемым диапазоном изменения значения переменных. Разумеется, для надежности разумно использовать наиболее «широкий» тип данных, однако при этом не следует забывать и о том, что системные ресурсы даже самого производительного компьютера не безграничны. Для работы с действительными числами используются типы float и double. С помощью этих типов реализуется формат числа с плавающей точкой. В этом формате действительное число задается посредством двух чисел: мантиссы. И показателя степени. Заданное таким образом число равно произведению мантиссы на десять в соответствующей второму числу степени. Поскольку размер в битах, выделяемый для типа double, в два раза больше размера для данных типа float, тип double называют типом действительных чисел двойной точности. На практике обычно используется тип double. Поскольку в Java для символьных данных (тип char) выделяется 16 бит, такая широта размаха позволяет охватить практически все имеющиеся и использующиеся на сегодня символы, включая китайские иероглифы. Этот демократизм, свойственный далеко не каждому языку программирования, является следствием курса разработчиков Java на создание универсального языка программирования, ориентированного на работу в Интернете. Все операторы Java можно разделить на четыре группы: арифметические, логические, побитовые и сравнения. Рассмотрим последовательно каждую группу операторов. Начнем с арифметических. Эти операторы перечислены в табл.1.2 Эти операторы имеют некоторые особенности. В первую очередь обращаем внимание на оператор деления /. Если операндами являются целые числа, в качестве значения возвращается результат целочисленного деления. Рассмотрим последовательность команд:

В данном примере переменная x получает значение 2.0, а не 2.5, как можно было бы ожидать. Дело в том, что сначала вычисляется выражение a/b. Поскольку операнды целочисленные, выполняется целочисленное деление. И только после этого полученное значение преобразуется к формату double и присваивается переменной x.

Для того чтобы при целочисленных операндах выполнялось обычное деление, перед выражением с оператором деления указывается в круглых скобках идентификатор типа double (или float). Например, так:

double x= (double) a/b;

Теперь значение переменной xравно 2.5.

В Java, как и в С++, есть группа упрощенных арифметических операторов с присваиванием. Если op один из операторов сложения, умножения, деления и вычисления остатка, то упрощенная форма этого оператора с присваиванием имеет вид op=. Это тоже бинарный оператор, как и оператор op, а команда вида xop=y является эквивалентом команды x=x op y. Еще два исключительно полезных унарных операторы инкремента (++) и декремента (—). Действие оператора декремента сводится к увеличению на единицу значения операнда, а оператор декремента на единицу уменьшает операнд. Другими словами, команда x++ эквивалентна команде x=x+1, а команда x — эквивалентна команде x=x-1. У операторов инкремента и декремента есть не только представленная здесь постфиксная форма (оператор следует после операнда: x++ или x—), но и префиксная (оператор располагается перед операндом: ++x или — -x). С точки зрения действия на операнд нет разницы в том, префиксная или постфиксная формы оператора использованы. Однако если выражение с оператором инкремента или декремента является частью более сложного выражения. Если использована префиксная форма оператора, сначала изменяется значение операнда, а уже после этого вычисляется выражение. Если использована постфиксная форма оператора, сначала вычисляется выражение, а затем изменяется значение операнда. Рассмотрим небольшой пример:

В этом случае после выполнения команд переменная n будет иметь значение 11, а переменная m — значение 10. На момент выполнения команды m=n++ значение переменной n равно 10. Поскольку в команде m=n++ использована, постфиксная

форма оператора инкремента, то сначала выполняется присваивание значения переменной m, а после этого значение переменной n увеличивается на единицу. Иной результат выполнения следующих команд:

Обе переменные (n и m) в этом случае имеют значение 11. Поскольку в команде m=++n использована префиксная форма инкремента, сначала на единицу увеличивается значение переменной n, а после этого значение переменной n присваивается переменной m. Следующую группу образуют логические операторы. Операндами логических операторов являются переменные и литералы типа boolean. Логические операторы Java перечислены в табл.1.3.

Интерфейс и пакеты. Пакет (package) — это некий контейнер, который используется для того, чтобы изолировать имена классов. Например, вы можете создать класс List, заключить его в пакет и не думать после этого о возможных конфликтах, которые могли бы возникнуть, если бы кто-нибудь еще создал класс с именем List.

Интерфейс — это явно указанная спецификация набора методов, которые должны быть представлены в классе, который реализует эту спецификацию. Реализация же этих методов в интерфейсе отсутствует. Подобно абстрактным классам интерфейсы обладают замечательным дополнительным свойством — их можно многократно наследовать. Конкретный класс может быть наследником лишь одного суперкласса, но зато в нем может быть реализовано неограниченное число интерфейсов.

Пакеты. Все идентификаторы, которые мы до сих пор использовали в наших примерах, располагались в одном и том же пространстве имен (name space). Это означает, что нам во избежание конфликтных ситуаций приходилось заботиться о том, чтобы у каждого класса было свое уникальное имя. Пакеты — это механизм, который служит как для работы с пространством имен, так и для ограничения видимости. У каждого файла java есть четыре одинаковых внутренних части, из которых мы до сих пор в наших примерах использовали только одну. Ниже приведена общая форма исходного файла Java.

одиночный оператор package (необязателен)

любое количество операторов import (необязательны)

одиночное объявление открытого (public) класса

любое количество закрытых (private) классов пакета (необязательны)

Первое, что может появиться в исходном файле Java — это оператор package, который сообщает транслятору, в каком пакете должны определяться содержащиеся в данном файле классы. Пакеты задают набор раздельных пространств имен, в которых хранятся имена классов. Если оператор package не указан, классы попадают в безымянное пространство имен, используемое по умолчанию. Если вы объявляете класс, как принадлежащий определенному пакету, например, package java. awt. image;

то и исходный код этого класса должен храниться в каталоге java/awt/image.

Ограничение доступа. Java предоставляет несколько уровней защиты, обеспечивающих возможность тонкой настройки области видимости данных и методов. Из-за наличия пакетов Java должна уметь работать еще с четырьмя категориями видимости между элементами классов: Подклассы в том же пакете.

Не подклассы в том же пакете. Подклассы в различных пакетах. Классы, которые не являются подклассами и не входят в тот же пакет.

Интерфейсы. Интерфейсы Java созданы для поддержки динамического выбора (resolution) методов во время выполнения программы. Интерфейсы похожи на классы, но в отличие от последних у интерфейсов нет переменных представителей, а в объявлениях методов отсутствует реализация. Класс может иметь любое количество интерфейсов. Все, что нужно сделать — это реализовать в классе полный набор методов всех интерфейсов. Сигнатуры таких методов класса должны точно совпадать с сигнатурами методов реализуемого в этом классе интерфейса. Интерфейсы обладают своей собственной иерархией, не пересекающейся с классовой иерархией наследования. Это дает возможность реализовать один и тот же интерфейс в различных классах, никак не связанных по линии классового наследования. Именно в этом и проявляется главная сила интерфейсов. Интерфейсы являются аналогом механизма множественного наследования в C++, но использовать их намного легче.

Работа со строками. В этой главе обсуждаются средства языка Java для работы со строками. В языках С и C++ отсутствует встроенная поддержка такого объекта, как строка. В них при необходимости передается адрес последовательности байтов, содержимое которых трактуется как символы до тех пор, пока не будет встречен нулевой байт, отмечающий конец строки. В пакет java. lang встроен класс, инкапсулирующий структуру данных, соответствующую строке. Этот класс, называемый String, не что иное, как объектное представление неизменяемого символьного массива. В этом классе есть методы, которые позволяют сравнивать строки, осуществлять в них поиск и извлекать определенные символы и подстроки. Класс StringBuffer используется тогда, когда строку после создания требуется изменять.

И String, и StringBuffer объявлены final, что означает, что ни от одного из этих классов нельзя производить подклассы. Это было сделано для того, чтобы можно было применить некоторые виды оптимизации позволяющие увеличить производительность при выполнении операций обработки строк.

Четыре принципа объектно-ориентированного программирования в Java

[an error occurred while processing this directive]

Конспект лекций по Java. Занятие 2

[an error occurred while processing this directive](none)

Отступление

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

Материал данного занятия может остаться не до конца понятым с первого раза. В этом случае можно порекомендовать продолжить знакомство со следующими темами и вернуться к нему позже.

JAVA — объектно-ориентированный язык программирования

Java является объектно-ориентированным языком программирования. Если сравнивать в этом смысле Java и C++, то между ними есть существенные различия.

C++ тоже является объектно-ориентированным языком. Но он является расширением C, который не является объектно-ориентированным. Из этого, в частности следует, что на C++ можно писать программы как в объектно-ориентированном стиле, так и в обычном. Т.е. на C++ можно программировать, не зная и не понимая объектно-ориентированного подхода. Можно даже смешивать оба подхода в едином продукте или пытаться программировать в объектно-ориентированном стиле, а тогда, когда это не получается, скатываться к традиционному программированию.

В Java так нельзя. В ней нет средств, позволяющих писать не объектно-ориентированные программы.

Из этого сразу следует один вывод. Нельзя научиться программировать на Java, не овладев основами объектно-ориентированного подхода.

5 принципов объектно-ориентированного подхода

  • Все является объектом
    Все данные программы хранятся в объектах. Каждый объект создается (есть средства для создания объектов), существует какое-то время, потом уничтожается.
  • Программа есть группа объектов, общающихся друг с другом
    Кроме того, что объект хранит какие-то данные, он умеет выполнять различные операции над своими данными и возвращать результаты этих операций. Теоретически эти операции выполняются как реакция на получение некоторого сообщения данным объектом. Практически это происходит при вызове метода данного объекта. Что такое метод и как он относится к объекту, мы рассмотрим позднее.
  • Каждый объект имеет свою память, состоящую из других объектов и/или элементарных данных.
    Объект хранит некоторые данные. Эти данные — это другие объекты, входящие в состав данного объекта и/или данные элементарных типов, такие как целое, вещественное, символ, и т.п.
  • Каждый объект имеет свой тип (класс)
    Т.е. в объектно-ориентированном подходе не рассматривается возможность создания произвольного объекта, состоящего из того, например, что мы укажем в момент его создания. Все объекты строго типизированы. Мы должны сначала описать (создать) тип (класс) объекта, указав в этом описании из каких элементов (полей) будут состоять объекты данного типа. После этого мы можем создавать объекты этого типа. Все они будут состоять из одних и тех же элементов (полей).
  • Все объекты одного и того же типа могут получать одни и те же сообщения
    Кроме описания структуры данных, входящих в объекты данного типа, описание типа содержит описание всех сообщений, которые могут получать объекты данного типа (всех методов данного класса). Более того, в описании типа мы должны задать не только перечень и сигнатуру сообщений данного типа, но и алгоритмы их обработки.

Реализация принципов объектно-ориентированного подхода в Java

Ссылки на объекты

В Java для манипулирования объектами в программном коде используются ссылки на объекты (handles). Ссылка хранит в себе некоторый адрес объекта в оперативной памяти.

Может быть несколько ссылок на один объект. На какой-то объект может вообще не быть ссылок (тогда он для нас безвозвратно потерян). Ссылка может не ссылаться ни на какой объект — пустая (null) ссылка. Не может быть ссылки в никуда или ссылки на какую-то произвольную область памяти. Как транслятор Java, так и JVM внимательно следят за тем, чтобы нельзя было создать ссылку на какую-то произвольную область памяти. Практически в Java это сделать невозможно.

Все ссылки имеют имя

Для манипулирования самими ссылками в программном коде необходимо как-то их обозначать. Это делается при помощи имени ссылки. Все ссылки, так или иначе, описываются, при этом каждой ссылке дается имя. Имена ссылок известны программе и встречаются в программном коде там, где нужно манипулировать объектами, на которые они ссылаются.

Все ссылки строго типизированы

При описании ссылки обязательно указывается ее тип. И эта ссылка может ссылаться только на объект данного типа. Есть определенные исключения из этого правила, связанные с наследованием классов. Мы рассмотрим это позднее.

Попытка присвоить ссылке адрес объекта не того класса пресекается как на этапе трансляции программы (выдаются ошибки трансляции), так и на этапе ее выполнения (возникает исключительная ситуация ClassCastException ).

Приведем пример описания ссылки

Здесь MyType — имя типа (как и ссылки, все типы имеют имя), ref — имя ссылки. После такого описания ссылке ref можно присвоить значение — адрес какого-то объекта типа MyType .

Создание объектов

Все объекты в Java создаются только явно, для чего используется операция new.

Здесь создается объект типа MyType и его адрес заносится в ref . Почему здесь использованы скобки после MyType , мы рассмотрим позже. Еще один пример

Здесь описание ссылки совмещено с инициализацией.

Класс — способ описания типа

Для описания типов в Java используется механизм классов. За исключением базовых (иначе — элементарных) типов ( int, char, float и др.) и интерфейсов (что это такое, мы рассмотрим позже), все остальные типы — это классы.

В простейшем случае описание класса выглядит так

Здесь >MyClass — имя класса. Внутри фигурных скобок находится тело класса.

Внутри тела класса описываются в произвольном порядке поля и методы класса.

Отступление

  • Существуют общепринятые правила именования классов, их полей и методов. Следование этим правилам улучшает «читабельность» программы. Имена классов принято начинать с большой буквы, а имена полей и методов — с маленькой. Если имя состоит из нескольких слов, то каждое новое слово начинают с большой буквы.
  • Общепринятых правил относительного размещения описаний полей и методов не существует. Но лучше, все же, размещать вместе все описания полей (например, в начале или в конце описания класса), а после или перед ними — описания методов.
  • В примере использован комментарий. В Java два типа комментариев. Все, что начинается с двух символов ‘/’, является комментарием и этот комментарий продолжается до конца данной строки. Все, что начинается с символов «/*» является комментарием, который должен быть закрыт символами «*/».

После того, как класс описан, мы можем создавать объекты (objects) класса (иначе — экземпляры класса, class instances).

Данные элементарных типов ссылками не являются

Ссылка хранит адрес объекта, а объект уже хранит какую-то содержательную информацию. В отличие от ссылок, данные элементарных типов являются самосодержащими, они сами хранят содержательную информацию.

Так можно продемонстрировать ссылку на объект.

А так — данное элементарного типа.

Тип Описатель Размер Комментарий
Логический boolean ?*
Символьный char 2 байта Unicode
Байтовый byte * 1 байт (-128 127)
Короткий целый short 2 байта (-2 15 — 2 15 -1)
Целый int 4 байта (-2 31 — 2 31 -1)
Длинный целый long 8 байт (-2 63 — 2 63 -1)
Вещественный float 4 байта
Вещественный двойной точности double 8 байт
Пустой void *
Поля класса и переменные программы

Как уже отмечалось, в классе можно описать поля класса (fields or instance variable). Поля класса определяют, из каких данных будут состоять объекты этого класса. Поля могут быть ссылками на другие объекты или элементарными данными.

В методах класса могут быть описаны переменные. Их не следует путать с полями класса. Как и поле класса, переменная может быть либо ссылкой, либо данным базового типа. Описание переменной выглядит точно так же, как и описание поля класса, за исключением того, что ряд описателей не применимы для переменных. Отличаются же (визуально) переменные от полей местом их описания. Поля класса описываются непосредственно в теле класса, на том же уровне вложенности, что и методы класса. Переменные описываются внутри методов. Пример:

Область видимости и время жизни переменных

В различных языках программирования существуют различные типы или классы переменных — локальные, глобальные, статические и т.п. В Java только один тип переменных — локальные переменные. Время жизни переменной в Java определяется правилом:

  • Переменная создается в точке ее описания и существует до момента окончания того блока, в котором находится данное описание.

В Java блок — это то, что начинается открывающей фигурной скобкой ‘<' и заканчивается закрывающей фигурной скобкой '>‘.

Областью видимости переменной (scope) является фрагмент программы от точки ее описания до конца текущего блока.

  • Область видимости — это статическое понятие, имеющее отношение к какому-то фрагменту текста программы. Время жизни, в отличие от области видимости, — это понятие динамики выполнения программы. Время жизни переменных в Java совпадает с их областью видимости с учетом отличия самих этих понятий.

Если в блоке, где описана данная переменная, вложены другие блоки, то переменная доступна в этих блоках (обычная практика языков программирования). Но, в отличие от многих других языков, в Java запрещено переопределять переменную во вложенных блоках (т.е. описывать другую переменную с тем же именем).

Рассмотрим примеры, демонстрирующие эти понятия.

Пример 1

Пример 2

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

Область видимости и время жизни объектов

Иная картина наблюдается с объектами. Объекты доступны в программе только через ссылки на них. Поэтому область видимости объекта определяется областью видимости ссылок на этот объект (на один объект может быть сколько угодно ссылок).

Время жизни объекта определяется следующим правилом.

  • Объект существует, пока существует хотя бы одна ссылка на этот объект.

Это правило, однако, не утверждает, что объект будет уничтожен, как только пропадет последняя ссылка на него. Просто такой объект становится недоступным и может быть уничтожен.

  • В Java нет явного уничтожения объектов. Объекты уничтожаются (говорят — утилизируются) сборщиком мусора (garbage collector), который работает в фоновом режиме параллельно с самой программой на Java.


Рассмотрим следующий фрагмент.

Здесь SomeType — это некоторый класс, localReference — локальная переменная-ссылка, globalReference — некоторая внешняя, по отношению к данному блоку, переменная или поле класса (из данного фрагмента нельзя сделать однозначный вывод, что это).

В этом фрагменте порождается объект класса SomeType и адрес этого объекта заносится в переменную localReference . После этого этот же адрес из localReference копируется в globalReference . По выходу из блока переменная localReference уничтожается, но переменная (или поле) globalReference продолжает существовать. Соответственно, продолжает существовать и порожденный объект.

Описание методов класса

В первом приближении методы класса (class methods) можно рассматривать как функции.

Описание метода выглядит следующим образом

Здесь — это один из базовых типов (см. таблицу выше) или пользовательский тип (т.е. некоторое имя класса). — это список, возможно пустой, параметров метода. — собственно программный код данного метода.

Каждый аргумент или параметр метода в данном описании — это пара » «. Аргументы отделяются друг от друга запятыми.

Описания методов расположены внутри класса, на том же уровне вложенности скобок, что и описание полей класса. Не может быть описания метода вне класса или внутри другого метода или блока.

Вызов методов

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

Формальное исключение составляет вызов метода класса из другого (или того же) метода данного класса, в этом случае объект можно не указывать. Но фактически объект и в данном случае имеется, это — тот объект, для которого был вызван вызывающий метод.

Рассмотрим это на примерах. Опишем класс SomeClass и в нем методы f и g .

Здесь описан метод f с одним параметром целого типа, возвращающий целое значение и метод g без параметров, не возвращающий никакого значения. Приведем примеры вызова этих методов из некоторого фрагмента программы.

В приведенном фрагменте фигурируют переменные (или поля класса) a, x, b и v . Переменные a и b должны быть описаны как ссылки с типом SomeClass , переменные x и v должны быть целочисленными.

Данный фрагмент демонстрирует, что объект, для которого вызывается метод, должен быть указан при помощи ссылки, имя которой записывается перед именем метода через точку.

При вызове метода класса из метода того же класса объект указывать не обязательно. Это, как указано выше, не нарушает того правила, что при вызове метода всегда должен быть определен объект, для которого этот метод вызывается. Просто в данном случае этот объект уже определен при вызове «вызывающего» метода и для него же вызывается «вызываемый» метод.

Доступ к полям класса

Поля класса не существуют сами по себе (за исключением статических). Они расположены внутри объекта класса. Поэтому при доступе к полю должен быть определен объект.

Как и в случае вызова метода, при обращении к полю класса извне класса объект должен быть указан явно (при помощи ссылки на объект) перед именем поля через точку. Например, если в классе SomeClass есть поля fld1 и fld2 , а obj — ссылка на объект класса SomeClass , то

являются примерами доступа к полям fld1 , fld2 .

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

Передача параметров

Для параметров функций, методов, процедур в программировании существует понятие тип передачи параметра. Например, существуют понятия «передача параметра по значению» и «передача параметра по ссылке». В Java существует всего один тип передачи параметров — передача по значению. Это означает, что при вызове метода ему передается текущее значение параметра. Внутри метода можно произвольно изменять параметр, но это никак не повлияет, скажем, на переменную, которая была указана в качестве параметра вызова. Дело в том, что при передаче параметра выделяется необходимая область памяти, куда копируется значение параметра, и внутри метода работа идет с этой копией. Она будет уничтожена при выходе из метода.

Это нужно хорошо себе представлять, в особенности, когда передаются ссылки на объекты.

Рассмотрим пример. Пусть ref — ссылка на объект, передаваемая в качестве параметра при вызове некоторого метода h(. ) .

Внутри метода h мы можем изменить параметр метода (т.е. присвоить ему ссылку на другой объект), но это никак не повлияет на саму ссылку ref , т.к. при вызове создается копия ref и изменяется именно она. С другой стороны мы можем внутри h менять данные того объекта, на который ссылается ref , и это реально отразится на этом объекте, т.к. создается копия только ссылки, но не самого объекта.

Концепции объектно-ориентированного программирования — ООП в Java

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

Основные принципы ООП

  1. Абстракция
  2. Инкапсуляция
  3. Полиморфизм
  4. Наследование
  5. Ассоциация
  6. Агрегирование
  7. Композиция

А теперь подробнее о каждом:

Абстракция

Абстракция — сокрытие внутренних деталей и описание вещей простыми словами. Например, метод складывает два целых числа, его внутренняя логика сокрыта от внешнего мира: он просто принимает на вход два числа и на выходе выдает результат сложения. Есть много способов достижения абстракции в объектно-ориентированном программировании, например: инкапсуляция и наследование.

Любая программа на Java уже является отличным примером абстракции: Java сама заботится о преобразовании команд на машинный язык и скрывает детали реализации от внешнего мира.

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

Инкапсуляция — метод достижения абстракции в объектно-ориентированном программировании. Инкапсуляция используется для ограничения доступа до членов класса и методов.

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

Полиморфизм

Говоря простыми словами, полиморфизм дает возможность объекту вести себя по-разному в различных ситуациях. Есть два типа полиморфизма: полиморфизм в процессе компиляции и выполнения.

Полиморфизм в процессе компиляции достигается за счет перегрузки метода. Смотрим пример ниже:

Здесь у нас несколько условных методов печати, которые имеют различное поведение. Имена методов одинаковые, однако аргументы методов (сигнатура методов) различные — это и называется перегрузкой метода. Компилятор должен определить метод вызова именно во время компиляции, поэтому это и называется полиморфизмом времени компиляции.

Полиморфизм времени выполнения достигается, когда у нас есть отношения «IS-A» между объектами. Подкласс должен переопределить метод суперкласса для достижения полиморфизма времени выполнения. Если мы смотрим в контексте суперкласса, то фактическая реализация класса определяется во время выполнения. Компилятор не в состоянии решить какой метод класса будет вызван. Это решение будет принято во время выполнения программы, отсюда и название: полиморфизм времени выполнения.

У нас есть 1 интерфейс и 2 класса, которые его реализуют, теперь посмотрим на полиморфизм времени выполнения:

В приведенном примере Java компилятор не знает точный класс, который реализует интерфейс. Это все будет определяться во время выполнения, следовательно, это пример полиморфизма времени выполнения.

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

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

В Java использутся ключевое слово extends для реализации наследования. Подробнее о наследовании в Java. Также рекомендую сравнительную статью Композиция vs Наследование.

Ассоциация

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

Агрегирование

Агрегирование представляет собой особый тип ассоциации. При агрегации объекты все также имеют свой жизненный цикл, однако ограничены отношением принадлежности «HAS-A», то есть отношение «часть-целое» между двумя объектами.

Композиция

Композиция представляет собой особую, более строгую форму агрегации. Это значит, что находящийся в отношениях «HAS-A» объект не может существовать сам по себе. Например, отношение «Дом-Комната» — здесь Комната не может существовать без Дома. Подробнее о композиции читайте в этой статье.

На этом краткий обзор концепций ООП завершен. Не упустил ли я чего-то важного в попытках сделать статью простой для понимания новичку? Если что — пишите в комментариях.

Четыре принципа объектно-ориентированного программирования в Java

Java относится к языкам объектно-ориентированного типа, поэтому, прежде чем приступать к написанию программ, следует познакомиться с принципами объектно-ориентированного программирования.

Основными концепциями здесь являются понятия объект и класс. Программа на языке Java состоит из классов, а каждый класс, в свою очередь, представляет объект реального света. Например, в качестве объекта возьмем робота, в этом случае, в классе Robot, представляющем этот объект, будут описаны его характеристики и поведение, то есть атрибуты и методы.

Класс Robot может состоять из таких атрибутов как:

можно придумать много других атрибутов.

Также класс Robot будет содержать методы, отвечающие за его поведение. К примеру, на начальном этапе робот умеет:

  • Спрашивать имя
  • Приветствовать по имени
  • Выполнять какую-либо работу

Абстракция

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

Далее рассмотрим 3 главных принципа, на которых строится объектно-ориентированное программирование:

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

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

Атрибуты или методы класса могут быть открытыми (public) или закрытыми (private). Закрытые атрибуты и методы могут быть доступны только внутри класса, в котором они находятся, они не доступны той части программного кода, которая находится вне этого класса. Открытые атрибуты и методы доступны, в том числе, и коду программы вне класса. Таким образом, открытые методы используются для предоставления контролируемого интерфейса к закрытым элементам класса.

Например, представим, что у нашего робота на голове находятся светодиоды, которые меняют цвет по голосовой команде «Измени цвет». Мы не можем ни как по-другому повлиять на смену цвета диодов, потому что этого не позволят настройки приватности. Мы можем повлиять на смену цвета, только при помощи конкретной голосовой команды, которая в данном случае является интерфейсом к светодиодам.

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

Наследование помогает избежать дублирования кода в случае, если нам нужно создать объект на основе уже существующего. В этом случае говорится, что новый объект (дочерний) унаследовал свойства уже существующего (родительского). Если атрибуты или поведение существующего объекта нужно частично изменить, то их можно просто переопределить.

Например, на основе уже существующего объекта Robot мы можем создать новый объект CoffeRobot, который будет варить кофе. Новый робот будет иметь все атрибуты и методы что и предыдущий, плюс содержать дополнительный метод «Варить кофе».

Полиморфизм

Если мы имеем объекты, которые принадлежат одной и той же ветви иерархии (были унаследованы), то для них можно использовать единый интерфейс, который будет для каждого объекта производить однотипное действие, но результат для каждого объекта будет различным (зависящим от этого конкретного объекта).

Например, если мы при помощи наследования создадим серию роботов разных типов (робот, который варит кофе; робот, который моет пол; робот, который поливает цветы), а потом каждому роботу дадим команду «работай», то каждый робот в ответ на ту же самую команду будет делать различные действия, в соответствии с его типом. То есть, единым интерфейсом здесь является объект Robot с методом «работать», а то, как именно он будет работать, зависит от его реализации.

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

Концепции объектно-ориентированного программирования (ООП) Java

В данном разделе будут изучены основные концепций объектно-ориентированного программирования (ООП) которые важно понимать для написания программ с помощью объекта-ориентированных языков как Java, C++, C#, Python, PHP и т.д. Подход ООП основывается на представлении системы как совокупности объектов. Таким образом объект является главным элементом программы.

Объект

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

Класс

Класс — это прототип или шаблон, который определяет объекты заданного типа. Это конструктор для создания объекта, который самостоятельно не занимает памяти, но при вызове выделяет память для объекта и инициализирует его переменные. Основные характеристики класса являются — состояние и поведение. Состояние (state) определяет свойства, который имеет объект, а поведение (behaviour) — его деятельность. В Java свойства объектов определяется как переменные (variables), а деятельность как методы (methods). К примеру, объект растение имеет такие параметры как возраст, высота, вид и географическое расположение, и такое поведение как расти, цвести, сбрасывать листву.

Экземпляр — это отдельная реализация класса. В нашем примере — это синяя фиалка возрастом год, которая расположена у вас на подоконнике.

Далее будет рассмотрены концепции ООП на основе Java

Наследование (Inheritance)

Наследование позволяет написание классов на основе уже существующего, при этом наследуется полностью или частично функциональность базового класса. Класс, от которого производится наследование, называется суперкласс, базовый или чаще родительский. Наследующий класс — потомок, производный, наследник или чаще дочерний класс. Наследование необходимо для избежания дублей, что тем самым улучшает эффективность и возможность повторного использования кода (code reuse)

В представленном ниже примере наследником родительского класса растение Plant является класс дерево tree с использованием ключевого слова расширяет (extends).

Во втором примере в классах собака dog , рыба fish и птица bird реализуются методы интерфейса двигаться move() с использованием ключевого слова реализует (implements). Сам метод объявлен в интерфейсе животное Animal .

Абстрагирование (Abstraction)

Абстракция позволяет скрыть внутренние процессы и детали, таким образом при реализации выделить значимые данные и исключить незначимые. Представим что необходимо написать программу Java, которая определяет скорость роста растения в зависимости от его территориального расположения. Такие данные как средняя продолжительность дня, количество выпадаемых осадков или тип почвы на данной территории может быть полезной, когда как плотность населения или наличие полезных ископаемых является избыточной информацией, которую можно исключить. На более высоком уровне, примером абстракции данных может служить отправка электронного сообщения, когда от пользователя скрыта информация по реализации протокола передачи данных и более низкоуровневых процессов.

Полиморфизм (Polymorphism)

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

Перегрузка метода (method overloading) — один из представлений полиморфизма, когда в одном классе имеется два или более метода с одинаковым именем, но с разными параметрами.

В примере выше, в родительском классе Plant метод grow() представлен два раза, каждый имеет разный набор аргументов.

Так как методы будут идентифицированы во время компиляции, этот тип называют compile time polymorphism.

Переопределение метода (method overriding) подразумевает наличие методов с тем же названием и параметрами, но различными реализациями. Этот тип полиморфизма используется в классах, которые имеют наследственную связь (IS-A).

К примеру, метод grow() представлен в двух классах, родительском Plant и дочернем tree .

Другой пример, задан родительский класс — животное Animal , реализующий метод двигаться move() , и есть три производных класса: собака dog , рыба fish и птица bird . Каждый подкласс будет иметь собственную реализацию движения — бежать, плавать и летать (см. результат второго примера).

Так как методы будут идентифицированы во время исполнения кода этот тип называют run time polymorphism.

Инкапсуляция (Encapsulation)

Инкапсуляция — это свойство программы, позволяющее реализовать абстракцию данных и ограничить доступ между компонентами программы, тем самым реализовать защиту данных от неправомерного использования. Для инкапсуляции используются модификаторы доступа (access modifiers) private, protected, public и default. Подробней про каждый можно рассмотреть в отдельной статье Модификаторы доступа Java

ГЛАВА 2. Объектно-ориентированное программирование в Java

Вся полувековая история программирования компьютеров, а может быть, и история всей науки — это попытка совладать со сложностью окружающего мира. Задачи, встающие перед программистами, становятся все более громоздкими, информация, которую надо обработать, растет как снежный ком. Еще недавно обычными единицами измерения информации были килобайты и мегабайты, а сейчас уже говорят только о гигабайтах и терабайтах. Как только программисты предлагают более-менее удовлетворительное решение предложенных задач, тут же возникают новые, еще более сложные задачи. Программисты придумывают новые методы, создают новые языки. За полвека появилось несколько сотен языков, предложено множество методов и стилей. Некоторые методы и стили становятся общепринятыми и образуют на некоторое время так называемую парадигму программирования.

Первые, даже самые простые программы, написанные в машинных кодах, составляли сотни строк совершенно непонятного текста. Для упрощения и ускорения программирования придумали языки высокого уровня: FORTRAN, Algol и сотни других, возложив рутинные операции по созданию машинного кода на компилятор. Те же программы, переписанные на языках высокого уровня, стали гораздо понятнее и короче. Но жизнь потребовала решения более сложных задач, и программы снова увеличились в размерах, стали необозримыми.

Возникла идея: оформить программу в виде нескольких, по возможности простых, процедур или функций, каждая из которых решает свой определенную задачу. Написать, откомпилировать и отладить небольшую процедуру можно легко и быстро. Затем остается только собрать все процедуры в нужном порядке в одну программу. Кроме того, один раз написанные процедуры можно затем использовать в других программах как строительные кирпичики. Процедурное программирование быстро стало парадигмой. Во все языки высокого уровня включили средства написания процедур и функций. Появилось множество библиотек процедур и функций на все случаи жизни.

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

Сложность стоящих леред программистами задач проявилась и тут: программу стали содержать сотни процедур, и опять оказались необозримыми. «Кирпичики» стали слишком маленькими. Потребовался новый стиль программирования,

В это же время обнаружилось, что удачная или неудачная структура исходных данных может сильно облегчить или усложнить их обработку. Одни исходные данные удобнее объединить в массив, для других больше подходит структура дерева или стека. Никлаус Вирт даже назвал свою книгу «Алгоритмы + структуры данных = программы».

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

Для того чтобы обеспечить максимальную независимость модулей друг от друга, надо четко отделить процедуры, которые будут вызываться другими модулями,— открытые (public) процедуры, от вспомогательных, которые обрабатывают данные, заключенные в этот модуль, — закрытых (private) процедур. Первые перечисляются в отдельной части модуля — интерфейсе (interface), вторые участвуют только в реализации (implementation) модуля. Данные, занесенные в модуль, тоже делятся на открытые, указанные в интерфейсе и доступные для других модулей, и закрытые, доступные только для процедур того же модуля. В разных языках программирования это деление производится по-разному. В языке Turbo Pascal модуль специально делится на интерфейс и реализацию в.языке С интерфейс выносится в отдельные «головные» (header) файлы. В даыке C++, кроме того, для описания интерфейса можно воспользоваться абстрактными классами. В языке Java есть специальная конструкция для описания интерфейсов, которая так и называется — interface, но можно написать и абстрактные классы.

Так возникла идея о скрытии, инкапсуляции (incapsulation) данных и методов их обработки. Подобные идеи периодически возникают в дизайне бытовой техники. То телевизоры испещряются кнопками и топорщатся ручками и движками на радость любознательному телезрителю, господствует «приборный» стиль, то вдруг все куда-то пропадает, а на панели остаются только кнопка включения и ручка громкости. Любознательный телезритель берется за отвертку.

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

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

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

Оказалось удобным сделать и обратное — разбить программу на модули так, чтобы она превратилась в совокупность взаимодействующих объектов. Так возникло объектно-ориентированное программирование (object-oriented programming), сокращенно ООП (OOP) — современная парадигма программирования.

В виде объектов можно представить совсем неожиданные понятия. Например, окно на экране дисплея — это объект, имеющий ширину width и высоту height , расположение на экране, описываемое обычно координатами (х, у) левого верхнего угла окна, а также шрифт, которым в окно выводится текст, скажем, Times New Roman, цвет фона color , несколько кнопок, линейки прокрутки и другие характеристики. Окно может перемещаться по экрану методом move() , увеличиваться или уменьшаться в размерах методом size() , сворачиваться в ярлык методом iconify() , как-то реагировать на действия мыши и нажатия клавиш. Это полноценный объект! Кнопки, полосы прокрутки и прочие элементы окна — это тоже объекты со своими размерами, шрифтами, перемещениями.


Разумеется, считать, что окно само «умеет» выполнять действия, а мы только даем ему поручения: «Свернись, развернись, передвинься», — это несколько неожиданный взгляд на вещи, но ведь сейчас можно подавать команды не только мышью и клавишами, но и голосом!

Идея объектно-ориентированного программирования оказалась очень плодотворной и стала активно развиваться. Выяснилось, что удобно ставить задачу сразу в виде совокупности действующих объектов — возник объектно-ориентированный анализ, ООА. Решили проектировать сложные системы в виде объектов — появилось объектно-ориентированное проектирование, ООП (OOD, object-oriented design).

Рассмотрим подробнее принципы объектно-ориентированного программирования.

Принципы объектно-ориентированного программирования

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

Описывая поведение какого-либо объекта, например автомобиля, мы строим его модель. Модель, как правило, не может описать объект полностью, реальные объекты слишком сложны. Приходится отбирать только те характеристики объекта, которые важны для решения поставленной перед нами задачи. Для описания грузоперевозок важной характеристикой будет грузоподъемность автомобиля, а для описания автомобильных гонок она не существенна. Но для моделирования гонок обязательно надо описать метод набора скорости данным автомобилем, а для грузоперевозок это не столь важно.

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

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

Описание каждой модели производится в виде одного или нескольких классов (classes). Класс можно считать проектом, слепком, чертежом, по которому затем будут создаваться конкретные объекты. Класс содержит описание переменных и констант, характеризующих объект. Они называются полями класса (class fields). Процедуры, описывающие поведение объекта, называются методами класса (class methods). Внутри класса можно описать и вложенные классы (nested classes) и вложенные интерфейсы. Поля, методы и вложенные классы первого уровня являются членами класса (class members). Разные школы объектно-ориентированного программирования предлагают разные термины, мы используем терминологию, принятую в технологии Java.

Вот набросок описания автомобиля:

int maxVelocity; // Поле, содержащее наибольшую скорость автомобиля

int speed; // Поле, содержащее текущую скорость автомобиля

int weight; // Поле, содержащее вес автомобиля

// автомобиля. Параметры х и у — не поля

int а = 1; // Локальная переменная — не поле

// Тело метода. Здесь описывается закон

// перемещения автомобиля в точку (х, у)

В Java нет вложенных процедур и функций, в теле метода нельзя описать другой метод.

После того как описание класса закончено, можно создавать конкретные объекты, экземпляры (instances) описанного класса. Создание экземпляров производится в три этапа, подобно описанию массивов. Сначала объявляются ссылки на объекты: записывается имя класса, и через пробел перечисляются экземпляры класса, точнее, ссылки на них.

Automobile Iada2110, fordScorpio, oka;

Затем операцией new определяются сами объекты, под них выделяется оперативная память, ссылка получает адрес этого участка в качестве своего значения.

Iada2110 = new Automobile();

fordScorpio = new Automobile();

oka = new Automobile(>;

На третьем этапе происходит инициализация объектов, задаются начальные значения. Этот этап, как правило, совмещается со вторым, именно для этого в операции new повторяется имя класса со скобками Automobile () . Это так называемый конструктор (constructor) класса, но о нем поговорим попозже.

Поскольку имена полей, методов и вложенных классов у всех объектов одинаковы, они заданы в описании класса, их надо уточнять именем ссылки на объект:

oka.maxVelocity = 350; // Почему бы и нет?

Напомним, что текстовая строка в кавычках понимается в Java как объект класса String . Поэтому можно написать

int strlen = «Это объект класса String».length();

Объект «строка» выполняет метод length() , один из методов своего класса string , подсчитывающий число символов в строке. В результате получаем значение strlen , равное 24. Подобная странная запись встречается в программах на Java на каждом шагу.

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

Не кажется ли вам, что класс Automobile сильно перегружен? Действительно, в мире выпущены миллионы автомобилей разных марок и видов. Что между ними общего, кроме четырех колес? Да и колес может быть больше или меньше. Не лучше ли написать отдельные классы для легковых и грузовых автомобилей, для гоночных автомобилей и вездеходов? Как организовать все это множество классов? На этот вопрос объектно-ориентированное программирование отвечает так: надо организовать иерархию классов.

Иерархия объектов давно используете для их классификации. Особенно детально она проработана в биологии. Все знакомы с семействами, родами и видами. Мы можем сделать описание своих домашних животных (pets): кошек (cats), собак (dogs), коров (cows) и прочих следующим образом:

Master person; // Хозяин животного

int weight, age, eatTimel]; // Вес, возраст, время кормления

int eat(int food, int drink, int time)< // Процесс кормления

if (time == eatTimefi]) person.getFood(food, drink);

// Метод потребления пищи

void voice(); // Звуки, издаваемые животным

Затем создаем классы, описывающие более конкретные объекты, связывая их с общим классом:

int mouseCatched; // число пойманных мышей

void toMouse(); // процесс ловли мышей

class Dog extends Pet< // Свойства собак:

void preserve(); // охранять

Заметьте, что мы не повторяем общие свойства, описанные в классе Pet . Они наследуются автоматически. Мы можем определить объект класса Dog и использовать в нем все свойства класса Pet так, как будто они описаны в классе Dog :

Dog tuzik = new Dog(), sharik = new Dog();

После этого определения можно будет написать

int p = sharik.eat (30, 10, 12);

А классификацию продолжить так:

class Pointer extends Dog < . >// Свойства породы Пойнтер

class Setter extends Dog < . >// Свойства сеттеров

Заметьте, что на каждом следующем уровне иерархии в класс добавляются новые свойства, но ни одно свойство не пропадает. Поэтому и употребляется слово extends — «расширяет» и говорят, что класс Dog — расширение (extension) класса Pet . С другой стороны, количество объектов при этом уменьшается: собак меньше, чем всех домашних животных. Поэтому часто говорят, что класс Dog — подкласс (sub >Pet , а класс Pet — суперкласс (super >Dog .

Часто используют генеалогическую терминологию: родительский класс, дочерний класс, класс-потомок, класс-предок, возникают племянники и внуки, вся беспокойная семейка вступает в отношения, достойные мексиканского сериала.

В этой терминологии говорят о наследовании (inheritance) классов, в нашем примере класс Dog наследует класс Pet .

Мы еще не определили счастливого владельца нашего домашнего зоопарка. Опишем его в классе Master. Делаем набросок:

String name; // Фамилия, имя

void getFood(int food, int drink); // Кормление

Хозяин и его домашние животные постоянно соприкасаются в жизни. Их взаимодействие выражается глаголами «гулять», «кормить», «охранять», «чистить», «ласкаться», «проситься» и прочими. Для описания взаимодействия объектов применяется третий принцип объектно-ориентированного программирования — обязанность или ответственность.

В нашем примере рассматривается только взаимодействие в процессе кормления, описываемое методом eat() . В этом методе животное обращается к хозяину, умоляя его применить метод getFood() .

В англоязычной литературе подобное обращение описывается словом message. Это понятие неудачно переведено на русский язык ни к чему не обязывающим словом «сообщение». Лучше было бы использовать слово «послание», «поручение» или даже «распоряжение». Но термин «сообщение» устоялся и нам придется его применять. Почему же не используется словосочетание «вызов метода», ведь говорят: «Вызов процедуры»? Потому что между этими понятиями есть, по крайней мере, триЪтличия.

  • Сообщение идет к конкретному объекту, знающему метод решения задачи, в примере этот объект — текущее значение переменной person . У каждого объекта свое текущее состояние, свои значения полей класса, и это может повлиять на выполнение метода.
  • Способ выполнения поручения, содержащегося в сообщении, зависит от объекта, которому оно послано. Один хозяин поставит миску с «Chappi», другой бросит кость, третий выгонит собаку на улицу. Это интересное свойство называется полиморфизмом (polymorphism) и будет обсуждаться ниже.
  • Обращение к методу произойдет только на этапе выполнения программы, компилятор ничего не знает про метод. Это называется «поздним связыванием» в противовес «раннему связыванию», при котором процедура присоединяется к программе на этапе компоновки.

Итак, объект sharik , выполняя свой метод eat () , посылает сообщение объекту, ссылка на который содержится в переменной person, с просьбой выдать ему определенное количество еды и питья. Сообщение записано в строке person.getFood(food, drink) .

Этим сообщением заключается контракт (contract) между объектами, суть которого в том, что объект sharik берет на себя ответственность (responsibility) задать правильные параметры в сообщении, а объект — текущее значение person — возлагает на себя ответственность применить метод кормления getFood() , каким бы он ни был.

Для того чтобы правильно реализовать принцип ответственности, применяется четвертый принцип объектно-ориентированного программирования — модульность (modularity).

Этот принцип утверждает — каждый класс должен составлять отдельный модуль. Члены класса, к которым не планируется обращение извне, должны быть инкапсулированы.

В языке Java инкапсуляция достигается добавлением модификатора private к описанию члена класса. Например:

private int mouseCatched;

private String name;

private void preserve();

Эти члены классов становятся закрытыми, ими могут пользоваться только экземпляры того же самого класса, например, tuzik может дать поручение

А если в классе Master мы напишем

private void getFood(int food, int drink);

то метод getFood() не будет найден, и несчастный sharik не сможет получить пищу. ,

В противоположность закрытости мы можем объявить некоторые члены класса открытыми, записав вместо слова private модификатор public , например:

public void getFood(int food, int drink);

К таким членам может обратиться любой объект любого класса.

В языке Java словами private, public и protected отмечается каждый член класса в отдельности.

Принцип модульности предписывает открывать члены класса только в случае необходимости. Вспомните надпись: «Нормальное положение шлагбаума — закрытое».

Если же надо обратиться к полю класса, то рекомендуется включить в класс специальные методы доступа (access methods), отдельно для чтения этого поля (get method) и для записи в это поле (set method). Имена методов доступа рекомендуется начинать со слов get и set , добавляя к этим словам имя поля. Для JavaBeans эти рекомендации возведены в ранг закона.

В нашем примере класса Master методы доступа к полю Name в самом простом виде могут выглядеть так:

public String getName()<

public void setName(String newName)

В реальных ситуациях доступ ограничивается разными проверками, особенно в set-методах, меняющих значения полей. Можно проверять тип вводимого значения, задавать диапазон значений, сравнивать со списком допустимых значений.

Кроме методов доступа рекомендуется создавать проверочные is-методы, возвращающие логическое значение true или false . Например, в класс Master можно включить метод, проверяющий, задано ли имя хозяина:

public boolean isEmpty()<

return name == null ? true : false;

и использовать этот метод для проверки при доступе к полю Name , например:

if (masterOl.isEmpty()) masterOl.setName(«Иванов»);

Итак, мы оставляем открытыми только методы, необходимые для взаимодействия объектов. При этом удобно спланировать классы так, чтобы зависимость между ними была наименьшей, как принято говорить в теории ООП, было наименьшее зацепление (low coupling) между классами. Тогда структура программы сильно упрощается. Кроме того, такие классы удобно использовать как строительные блоки для построения других программ.

Напротив, члены класса должны активно взаимодействовать друг с другом, как говорят, иметь тесную функциональную связность (high cohestion). Для этого в класс следует включать все методы, описывающие поведение моделируемого объекта, и только такие методы, ничего лишнего. Одно из правил достижения сильной функциональной связности, введенное Карлом Ли-берхером (Karl J. Lieberherr), получило название закон Деметра. Закон гласит: «в методе т() класса А следует использовать только методы класса А, методы классов, к которым принадлежат аргументы метода т(), и методы классов, экземпляры которых создаются внутри метода m ().

Объекты, построенные по этим правилам, подобны кораблям, снабженным всем необходимым. Они уходят в автономное плавание, готовые выполнить любое поручение, на которое рассчитана их конструкция.

Будут ли закрытые члены класса доступны его наследникам? Если в классе Pet написано

private Master person;

то можно ли использовать sharik.person ? Разумеется, нет. Ведь в противном случае каждый, интересующийся закрытыми полями класса А , может расширить его классом B , и просмотреть закрытые поля класса А через экземпляры класса B .

Когда надо разрешить доступ наследникам класса, но нежелательно открывать его всему миру, тогда в Java используется защищенный (protected) доступ, отмечаемый модификатором protected , например, объект sharik может обратиться к полю person родительского класса pet , если в классе Pet это поле описано так:

protected Master person;

Следует сразу сказать, что на доступ к члену класса влияет еще и пакет, в котором находится класс, но об этом поговорим в следующей главе.

Из этого общего схематического описания принципов объектно-ориентированного программирования видно, что язык Java позволяет легко воплощать все эти принципы. Вы уже поняли, как записать класс, его поля и методы, как инкапсулировать члены класса, как сделать расширение класса и какими принципами следует при этом пользоваться. Разберем теперь подробнее правила записи классов и рассмотрим дополнительные их возможности.

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

Самый основной, базовый и самый великий : принцип программирования принцип KISS — не нуждается в разъяснений : и переводе: «Keep It Simple, Stupid!»

Как описать класс и подкласс

Итак, описание класса начинается со слова class, после которого записывается имя класса. Соглашения «Code Conventions» рекомендуют начинать имя класса с заглавной буквы.

Перед словом class можно записать модификаторы класса ( >public, abstract, final, strictfp . Перед именем вложенного класса можно поставить, кроме того, модификаторы protected, private, static . Модификаторы мы будем вводить по мере изучения языка.

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

При описании поля указывается его тип, затем, через пробел, имя и, может быть, начальное значение после знака равенства, которое можно записать константным выражением. Все это уже описано в главе 1.

Описание поля может начинаться с одного или нескольких необязательных модификаторов public, protected, private, static, final, transient, volatile . Если надо поставить несколько модификаторов, то перечислять их JLS рекомендует в указанном порядке, поскольку некоторые компиляторы требуют определенного порядка записи модификаторов. С модификаторами мы будем знакомиться по мере необходимости.

При описании метода указывается тип возвращаемого им значения или слово void , затем, через пробел, имя метода, потом, в скобках, список параметров. После этого в фигурных скобках расписывается выполняемый метод.

Описание метода может начинаться с модификаторов public, protected, private, abstract, static, final, synchronized, native, strictfp . Мы будем вводить их по необходимости.

В списке параметров через запятую перечисляются тип и имя каждого параметра. Перед типом какого-либо параметра может стоять модификатор final . Такой параметр нельзя изменять внутри метода. Список параметров может отсутствовать, но скобки сохраняются.

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

В листинге 2.1 показано, как можно оформить метод деления пополам для нахождения корня нелинейного уравнения из листинга 1.5.

Листинг 2.1. Нахождение корня нелинейного уравнения методом бисекцйи

private static double final EPS = le-8; // Константа

private double a = 0.0, b = 1.5, root; // Закрытые поля

public double getRoot(> // Метод доступа

private double f(double x)

return x*x*x — 3*x*x + 3; // Или что-то другое

// метод работает с полями экземпляра

double у = 0.0; // Локальная переменная — не поле

root = 0.5 *(а + b); у = f(root);

// Корень найден. Выходим из цикла

// Если на концах отрезка [a; root]

// функция имеет разные знаки:

// значит, корень здесь

// Переносим точку b в точку root


//В противном случае:

// переносим точку а в точку root

// Продолжаем, пока [а; Ь] не станет мал

public static void main(String[] args)<

Bisection2 b2 = new Bisection2();

b2.getRoot() + // Обращаемся к корню через метод доступа

В описании метода f() сохранен старый, процедурный стиль: метод получает аргумент, обрабатывает его и возвращает результат. Описание метода bisect о выполнено в духе ООП: метод активен, он сам обращается к полям экземпляра b2 и сам заносит результат в нужное поле. Метод bisect () — это внутренний механизм класса Bisection2, поэтому он закрыт (private).

Имя метода, число и типы параметров образуют сигнатуру (signature) метода. Компилятор различает методы не по их именам, а по сигнатурам. Это позволяет записывать разные методы с одинаковыми именами, различающиеся числом и/или типами параметров.

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

Например, в классе Automobile мы записали метод moveTo(int x, int у) , обозначив пункт назначения его географическими координатами. Можно определить еще метод moveTo (string destination) для указания географического названия пункта назначения и обращаться к нему так:

Такое дублирование методов называется перегрузкой (overloading). Перегрузка методов очень удобна в использовании. Вспомните, в главе 1 мы выводили данные любого типа на экран методом printin() не заботясь о том, данные какого именно типа мы выводим. На самом деле мы использовали разные методы t одним и тем же именем printin , даже не задумываясь об этом. Конечно, все эти методы надо тщательно спланировать и заранее описать в классе. Это и сделано в классе Printstream, где представлено около двадцати методов print() и println() .

Если же записать метод с тем же именем в подклассе, например:

class Truck extends Automobile<

void moveTo(int x, int y)<

то он перекроет метод суперкласса. Определив экземпляр класса Truck , например:

Truck gazel = new Truck();

и записав gazei.moveTo(25, 150) , мы обратимся к методу класса Truck . Произойдет переопределение (overriding) метода.

При переопределении права доступа к методу можно только расширить. Открытый метод public должен остаться открытым, защищенный protected может стать открытым.

Можно ли внутри подкласса обратиться к методу суперкласса? Да, можно, если уточнить имя метода, словом super , например, super.moveTo(30, 40) . Можно уточнить и имя метода, записанного в этом же классе, словом this , например, this.moveTo (50, 70) , но в данном случае это уже излишне. Таким же образом можно уточнять и совпадающие имена полей, а не только методов.

Данные уточнения подобны тому, как мы говорим про себя «я», а не «Иван Петрович», и говорим «отец», а не «Петр Сидорович».

Переопределение методов приводит к интересным результатам. В классе Pet мы описали метод voice() . Переопределим его в подклассах и используем в классе chorus , как показано в листинге 2.2.

Листинг 2.2. Пример полиморфного метода

Объектно-ориентированное программирование

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

Из общих объектов создаются другие, более специализированные. Механизм создания таких подобъектов называется наследованием. В итоге данные программы представляют из себя объектную модель — дерево объектов, начиная с самого верхнего наиболее абстрактного и общего объекта.

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

Примером объектно-ориентированных языков являются: Object Pascal, C++, Java.

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

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

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

Объект состоит из следующих трех частей:

— состояние (переменные состояния);

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

Класс (class) – это группа данных и методов (функций) для работы с этими данными. Это шаблон. Объекты с одинаковыми свойствами, то есть с одинаковыми наборами переменных состояния и методов, образуют класс. Объект (object) – это конкретная реализация, экземпляр класса. В программировании отношения объекта и класса можно сравнить с описанием переменной, где сама переменная (объект) является экземпляром какого-либо типа данных (класса).

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

Теоретический подход. Класс — это один из вариантов описания сущности, которая в теории программирования именуется абстрактным типом данных. Класс определяет скрытую внутреннюю структуру некоторого значения, а также набор операций, применимых к данному значению.

Практический подход. В современных объектно-ориентированных языках программирования (php, Java, C++, Oberon, Python, Ruby, Smalltalk, Object Pascal) создание класса сводится к написанию некоторой структуры, содержащей набор полей и методов. Практически класс может пониматься как некий шаблон, по которому создаются объекты — экземпляры данного класса. Экземпляры одного класса созданы по одному шаблону, поэтому имеют один и тот же набор полей и методов.

Отношения между классами:

— Наследование (Генерализация) — объекты дочернего класса наследуют все свойства родительского класса.

— Ассоциация — объекты классов вступают во взаимодействие между собой.

— Агрегация — объекты одного класса входят в объекты другого.

— Композиция — объекты одного класса входят в объекты другого и зависят друг от друга по времени жизни.

— Класс-Метакласс — отношение, при котором экземплярами одного класса являются другие классы.

Виды классов:

— базовый (родительский) класс;

— производный класс (наследник, потомок);

Класс – это структурный тип данных, который включает описание полей данных, а также процедур и функций, работающих с этими полями данных. Применительно к классам такие процедуры и функции получили название методов.

Методы – инкапсулированные в классе процедуры и функции, то есть способы работы с данными.

В основу классов и объектно-ориентированного программирования положены три принципа – инкапсуляция, наследование и полиморфизм.

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

Чаще всего инкапсуляция выполняется посредством скрытия информации, то есть маскировкой всех внутренних деталей, не влияющих на внешнее поведение. Обычно скрываются и внутренняя структура объекта и реализация его методов.

Цели инкапсуляции:

§ предельная локализация изменений при необходимости таких изменений,

§ прогнозируемость изменений (какие изменения в коде надо сделать для заданного изменения функциональности) и прогнозируемость последствий изменений.

Инкапсуляция – это процесс отделения друг от друга элементов объекта, определяющих его устройство и поведение. Часто инкапсуляция может быть достигнута простейшими организационными мерами: знание того, что «вот так-то делать нельзя» иногда является самым эффективным средством инкапсуляции!

Инкапсуляция – комбинирование записей с процедурами и функциями, манипулирующими полями этих записей, формирует новый тип данных — объект.

Инкапсуляция – изолирование составляющих класса (полей, методов и свойств) от остальных частей программы.

Суть инкапсуляции: Переменные состояния объекта скрыты от внешнего мира. Изменение состояния объекта (его переменных) возможно ТОЛЬКО с помощью его методов (операций). Почему это так важно? Этот принцип позволяет защитить переменные состояния объекта от неправильного их использования.

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

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

Наследование – это процесс, посредством которого, один объект может наследовать свойства другого объекта и добавлять к ним черты, характерные только для него. Смысл и универсальность наследования заключается в том, что не надо каждый раз заново (с нуля) описывать новый объект, а можно указать родителя (базовый класс) и описать отличительные особенности нового класса. В результате, новый объект будет обладать всеми свойствами родительского класса плюс своими собственными отличительными особенностями.

Наследование – представляет собой возможность построения иерархии объек­тов с использованием наследования их характеристик.

Наследование. Наследование – это такое свойство объекта, которое позволяет ему использовать поля и методы объекта родителя, без описания их в своей структуре.

Наследование – возможность создания новых классов на базе имеющихся с возможностью использования их составляющих. Объект, принадлежащий классу-потомку, может использовать поля, свойства и методы класса-родителя и новые составляющие своего класса.

Если в классе-потомке описан новый метод, одноименный с методом класса-родителя, то «говорят», что в потомке «перекрыт» метод родителя. Другими словами, класс-наследник реализует спецификацию уже существующего класса (базовый класс). Это позволяет обращаться с объектами класса-наследника точно так же, как с объектами базового класса. При создании иерархии классов некоторые свойства объектов, сохраняя названия, изменяются по сути.

Для реализации таких иерархий в языке программирования предусмотрен полиморфизм. Слово полиморфизм имеет греческое происхождение и переводится как «имеющий много форм».

Полиморфизм. Присваивание действию одного имени, которое затем совместно используется вниз и вверх по иерархии объектов, причем каждый объект иерархии выполняет это действие способом, именно ему подходящим.

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

В терминах ООП можно сказать, что все типы интерфейсных кнопок имеют способность изображения самих себя на экране. Однако способ (процедура) является различным для каждого типа кнопки. Простая кнопка рисуется на экране с помощью процедуры «вывод изображения простой кнопки», кнопка-переключатель рисуется на экране с помощью процедуры «вывод изображения кнопки-переключателя» и т.д.

Таким образом, существует единственное для всего перечня интерфейсных кнопок действие (вывод изображения кнопки на экран), которое реализуется специфическим для каждой кнопки способом. Это и является проявлением полиморфизма.

Полиморфизм – способность классов решать похожие задачи разными способами. При перекрытии метода родителя в потомке реализуется новый алгоритм решения задачи. Получается, что в объекте-родителе и объекте-потомке действуют два одноименных метода, имеющих разную алгоритмическую основу.

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

В общем смысле, концепцией полиморфизма является идея «один интерфейс, множество методов». Это означает, что можно создать общий интерфейс для группы близких по смыслу действий.

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

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

Механизм работы ООП в таких случаях можно описать примерно так: при вызове того или иного метода класса сначала ищется метод у самого класса. Если метод найден, то он выполняется и поиск этого метода на этом завершается. Если же метод не найден, то обращаемся к родительскому классу и ищем вызванный метод у него. Если найден – поступаем как при нахождении метода в самом классе. А если нет – продолжаем дальнейший поиск вверх по иерархическому дереву. Вплоть до корня (верхнего класса) иерархии.

Не нашли то, что искали? Воспользуйтесь поиском:

Четыре принципа объектно-ориентированного программирования. Их реализация в Visual Basic

Читайте также:

  1. CHILDREN WITH VISUAL IMPAIRMENTS
  2. Read a short article about visual aid
  3. USING VISUAL SUPPORT
  4. Visual aids
  5. Visual Basic 2010
  6. Visual Basic for Application (VBA)
  7. Адвокатская монополия в свете конституционного принципа баланса интересов.
  8. Активные четырехполюсники
  9. Алгоритм создания программы-калькулятора на языке Visual Basic.
  10. Аспекты проблемы анализа и их реализация в программных продуктах
  11. В действительности четыре категории противоречат друг другу
  12. В1. Основні поняття про проекти Visual Studio 200

Диалоговое окно ввода информации (InputBox)

Достаточно часто в диалоговом окне необходимо не только нажать кнопки выбора действия, но и ввести определенную информацию, которая затем анализируется программой. Для выполнения такого рода действий в Visual Basic можно использовать диалоговое окно ввода информацииInputBox(рис. 7.20). Функция InputBox имеет следующий синтаксис:

InputBox (prompt [, title] [, default] [, xpos] [, ypos] [, helpfile, context])

  • prompt — текст сообщения в диалоговом окне. Максимальная длина текста 1024 символа. В этот текст можно вставить в качестве разделителей строк перевод каретки Chr(13), перевод строки Chr(lO) или их комбинацию;
  • title — текст заголовка диалогового окна;
  • default — значение текстового поля ввода по умолчанию. Если параметр отсутствует, строка остается пустой;
  • xpos — позиция по горизонтали левого верхнего угла диалогового окна относительно левого верхнего угла экрана. По умолчанию присваивается значение, соответствующее середине экрана;
  • ypos — позиция по вертикали левого верхнего угла диалогового окна относительно левого верхнего угла экрана. По умолчанию присваивается значение, соответствующее середине экрана;
  • helpfile — ссылка на файл справочной системы;
  • context — ссылка на содержание в файле справочной системы.

Для примера введите в командном окне среды проектированияImmediateследующую команду:

strUserTest = InputBox («Введите пароль», «Запуск приложения», «****»)

В результате получите диалоговое окно, показанное на рис. 7.20.

В отличие от диалогового окнаMsgBox, в окнеInputBox всегда имеются только две кнопки управления:ОК иCancel. Кнопка ОК подтверждает ввод данных, кнопкаCancel — закрывает диалоговое окно без ввода данных.

VB и объектно-ориентированное программирование

Как уже упоминалось в первой главе, Visual Basic, в определен­ной степени, относится к объектно-ориентированным языкам прог­раммирования. ООП – это методология разработки программ, основанная на представлении ее в виде совокупности объектов, каждый из которых является реализацией определенного класса. В ООП объект представляет собой элемент приложения, например, лист, форму, меню. Программный код и данные структурируются так, чтобы имитировалось поведение фактически существующих объектов. Принадлежность языка программирования к ООП определяется использованием в нем четырех основных принципов: абстрагирования, инкапсуляции, наследования и полиморфизма.

· Абстрагирование – это простое описание сложных объектов окружающей среды. Причем описание должно быть очень точ­ным, чтобы не возникло ошибочного представления о другом объекте. Так, не надо создавать специальных программ для отображения графической информации, а достаточно просто использовать объект Image – это абстрактный объект, содержа­щий все необходимое для отображения изображений.

· Инкапсуляция – это возможность эффективно изолировать данные, методы и свойства от остальной части программы и от случайного разрушения извне.

· Наследование – это возможность создания новых классов на базе уже имеющихся, при этом вновь созданные классы как бы наследуют методы и свойства родительского класса. Наследова­ние позволяет создавать многократно используемый код.

· Полиморфизм – позволяет многократно определять одноимен­ные методы в иерархии классов. Полиморфизм реализуется технологией Automation через механизм позднего связывания, предоставляющего возможность использовать объекты других приложений в качестве компонентов собственных приложений

4. Классы. Интерфейс классов. Создание динамических библиотек на основе классов в Visual Basic. Регистрация библиотек.

Класс используется для задания структуры объекта. Это своего рода шаблон, на основе которого будут созданы объекты. Для ими­тации поведения реальных объектов в классах должны быть пред­ставлены принципы действия (методы) и данные объектов (свойства).

Чтобы создать модуль класса, следует воспользоваться командой Project\Add Class Module. В отрывшемся диалоге Add Class Module на закладке New выбрать шаблон Class Module (для создания пустого окна модуля класса и заполнения его самостоятельно пользователем) или шаблон VB Class Builder (для запуска мастера классов, рис.85). Мастер классов может быть установлен в виде команды меню VB (Add-Ins\Class Builder Utility), если предварительно загрузить утили­ту VB6 Class Builder Utility в память, используя диалог Add-In… Manager, открывающийся командой Add-Ins\Add-In…Manager. Для редактирования существующих классов используется любой из двух последних способов.

Находясь в диалоге Class Builder с помощью команды File\New\ Class следует задать имя класса в поле Name, а в списке Base on – имя класса родителя (принцип наследования). После чего появится возможность создавать свойства (Property), методы (Method), события (Event) и структуры целочисленных кон­стант (Enum). Кроме классов мастер может создавать семейства (Collection).

При создании свойств VB использует процедуры свойств (Property), которые во многом похожи на общие процедуры (Sub) и функции (Function). Чтобы они были видимы в пределах контейнера, их сле­дует объявить как Public.

Вы уже знаете, что значе­ния свойств можно как счи­тывать, так и устанавливать, поэтому для одного свойства требуется две процедуры с одним и тем же именем: одна для чтения, другая для записи значения в свойство. Для этого в заголовке процедур Property используются ключевые слова Let (присвоить) и Get (получить). Разделение процедур свойств на процедуры присваивания и процеду­ры считывания позволяет создавать свойства, доступные только для чтения или только для изменения (принцип инкапсуляции). Мастер классов (Class Builder) позволяет добавлять свойства к существую­щему классу с помощью команды File\New\Property (рис.86) или кнопки . В этом окне следует задать имя, тип данных и доступ­ность свойств. Если будет установлена одна из опций Public Property или Friend Property, то мастер, после выполнения команды File\ Update Project, добавит в модуль класса три процедуры Property (Let, Get и Set), а также внутреннюю переменную для хранения зна­чения свойства. Например, окно класса после создания свойства с именем CurX типа Variant будет содержать следующий код.

Private mvarCurX As Variant ‘local copy

Дата добавления: 2014-12-23 ; Просмотров: 325 ; Нарушение авторских прав? ;

Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет

Объектно-ориентированное программирование в Java

Вся полувековая история программирования компьютеров, а может быть, и история всей науки — это попытка совладать со сложностью окружающего мира. Задачи, встающие перед программистами, становятся все более громоздкими, информация, которую надо обработать, растет как снежный ком. Еще недавно обычными единицами измерения информации были килобайты и мегабайты, а сейчас уже говорят только о гигабайтах и терабайтах. Как только программисты предлагают более-менее удовлетворительное решение предложенных задач, тут же возникают новые, еще более сложные задачи. Программисты придумывают новые методы, создают новые языки. За полвека появилось несколько сотен языков, предложено множество методов и стилей. Некоторые методы и стили становятся общепринятыми и образуют на некоторое время так называемую парадигму программирования.

Принципы объектно-ориентированного программирования

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

1. Абстракция-Описание каждой модели производится в виде одного или нескольких классов (classes). Класс можно считать проектом, слепком, чертежом, по которому затем будут создаваться конкретные объекты. Класс содержит описание переменных и констант, характеризующих объект. Они называются полями класса (class fields). Процедуры, описывающие поведение объекта, называются методами класса (class methods). Внутри класса можно описать и вложенные классы (nested classes) и вложенные интерфейсы. Поля, методы и вложенные классы первого уровня являются членами класса (class members). Разные школы объектно-ориентированного программирования предлагают разные термины, мы используем терминологию, принятую в технологии Java.

2. Иерархия объектов давно используете для их классификации. Особенно детально она проработана в биологии. Все знакомы с семействами, родами и видами. Мы можем сделать описание своих домашних животных (pets): кошек (cats), собак (dogs), коров (cows) и прочих следующим образом.

3. Модульность-Этот принцип утверждает — каждый класс должен составлять отдельный модуль. Члены класса, к которым не планируется обращение извне, должны быть инкапсулированы.

4. Принцип KISS -Самый основной, базовый и самый великий : принцип программирования — принцип KISS — не нуждается в разъяснений : и переводе: «Keep It Simple, Stupid!»

Принципы построения графического интерфейса

Есть много различных графических систем: MS Windows, X Window System, Macintosh. В каждой из них свои правила построения окон и их компонентов: меню, полей ввода, кнопок, списков, полос прокрутки. Эти правила сложны и запутанны. Графические API содержат сотни функций.

Для облегчения создания окон и их компонентов написаны библиотеки классов: MFC, Motif, OpenLook, Qt, Tk, Xview, OpenWindows и множество других. Каждый класс такой библиотеки описывает сразу целый графический компонент, управляемый методами этого и других классов.

В технологии Java дело осложняется тем, что приложения Java должны работать в любой или хотя бы во многих графических средах. Нужна библиотека классов, независимая от конкретной графической системы. В первой версии JDK задачу рещили следующим образом: были разработаны интерфейсы, содержащие методы работы с графическими объектами. Классы библиотеки AWT реализуют эти интерфейсы для создания приложений. Приложения Java используют данные методы для размещения и перемещения графических объектов, изменения их размеров, взаимодействия объектов.

Библиотека классов Java, основанных на peer-интерфейсах, получила название AWT (Abstract Window Toolkit). При выводе объекта, созданного в приложении Java и основанного на peer-интерфейсе, на экран создается парный ему (peer-to-peer) объект графической подсистемы операционной системы, который и отображается на экране. Эти объекты тесно взаимодействуют во время работы приложения. Поэтому графические объекты AWT в каждой графической среде имеют вид, характерный для этой среды: в MS Windows, Motif, OpenLook, OpenWindows, везде окна, созданные в AWT, выглядят как «родные» окна.

Именно из-за такой реализации peer-интерфейсов и других «родных» методов, написанных, главным образом, на языке C++, приходится для каждой платформы выпускать свой вариант JDK.

В версии JDK 1.1 библиотека AWT была переработана. В нее добавлена возможность создания компонентов, полностью написанных на Java и не зависящих от peer-интерфейсов. Такие компоненты стали называть «легкими» (lightweight) в отличие от компонентов, реализованных через peer-интерфейсы, названных «тяжелыми» (heavy).

«Легкие» компоненты везде выглядят одинаково, сохраняют заданный при создании вид (look and feel). Более того, приложение можно разработать таким образом, чтобы после его запуска можно было выбрать какой-то определенный вид: Motif, Metal, Windows 95 или какой-нибудь другой, и сменить этот вид в любой момент работы.

Эта интересная особенность «легких» компонентов получила название PL&F (Pluggable Look and Feel) или «plaf.

Была создана обширная библиотека «легких» компонентоэ Java, названная Swing. В ней были переписаны все компоненты библиотеки AWT, так что библиотека Swing может использоваться самостоятельно, несмотря на то, что все классы из нее расширяют классы библиотеки AWT.

Библиотека классов Swing поставлялась как дополнение к JDK 1.1. В состав Java 2 SDK она включена как основная графическая библиотека классов, реализующая идею «100% Pure Java», наряду с AWT.

В Java 2 библиотека AWT значительно расширена добавлением новых средств рисования, вывода текстов и изображений, получивших название Java 2D, и средств, реализующих перемещение текста методом DnD (Drag and Drop).

Кроме того, в Java 2 включены новые методы ввода/вывода Input Method Framework и средства связи с дополнительными устройствами ввода/вывода, такими как световое перо или клавиатура Бройля, названные Accessibility.

Все эти средства Java 2: AWT, Swing, Java 2D, DnD, Input Method Framework и Accessibility составили библиотеку графических средств Java, названную JFC (Java Foundation Classes).

Описание каждого из этих средств составит целую книгу, поэтому мы вынуждены ограничиться представлением только основных средств библиотеки AWT.

Компонент и контейнер

Основное понятие графического интерфейса пользователя (ГИП) — компонент (component) графической системы. В русском языке это слово подразумевает просто составную часть, элемент чего-нибудь, но в графическом интерфейсе это понятие гораздо конкретнее. Оно означает отдельный, полностью определенный элемент, который можно использовать в графическом интерфейсе независимо от других элементов. Например, это поле ввода, кнопка, строка меню, полоса прокрутки, радиокнопка. Само окно приложения — тоже его компонент. Компоненты могут быть и невидимыми, например, панель, объединяющая компоненты, тоже является компонентом.

Вы не удивитесь, узнав, что в AWT компонентом считается объект класса Component или объект всякого класса, расширяющего класс component. В классе component собраны общие методы работы с любым компонентом графического интерфейса пользователя. Этот класс — центр библиотеки AWT.

Каждый компонент перед выводом на экран помещается в контейнер (container). Контейнер «знает», как разместить компоненты на экране. Разумеется, в языке Java контейнер — это объект класса Container или всякого его расширения. Прямой наследник этого класса — класс jcomponent — вершина иерархии многих классов библиотеки Swing.

Создав компонент — объект класса Component или его расширения, следует добавить его к предварительно созданному объекту класса container или его расширения одним из методов add ().

Класс Container сам является невидимым компонентом, он расширяет класс Component. Таким образом, в контейнер наряду с компонентами можно помещать контейнеры, в которых находятся какие-то другие компоненты, достигая тем самым большой гибкости расположения компонентов.

Основное окно приложения, активно взаимодействующее с операционной системой, необходимо построить по правилам графической системы. Оно должно перемещаться по экрану, изменять размеры, реагировать на дейст-

вия мыши и клавиатуры. В окне должны быть, как минимум, следующие стандартные компоненты.

· Строка заголовка (title bar), с левой стороны которой необходимо разместить кнопку контекстного меню, а с правой — кнопки сворачивания и разворачивания окна и кнопку закрытия приложения.

· Необязательная строка меню (menu bar) с выпадающими пунктами меню.

· Горизонтальная и вертикальная полосы прокрутки (scrollbars).

· Окно должно быть окружено рамкой (border), реагирующей на действия мыши.

Окно с этими компонентами в готовом виде описано в классе Frame. Чтобы создать окно, достаточно сделать свой класс расширением класса Frame, как показано в листинге 8.1. Всего восемь строк текста и окно готово.

Мастер Йода рекомендует:  Редиректы с использованием HTTPS
Добавить комментарий