library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В....

340
МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ПУТЕЙ СООБЩЕНИЯ (МИИТ) Институт управления и информационных технологий Кафедра «Вычислительные системы и сети» А. В. МИХАЙЛЮК ВВЕДЕНИЕ В ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ Рекомендовано редакционно-издательским советом университета в качестве учебного пособия для студентов, обучающихся по направлению «Информатика и вычислительная техника» МОСКВА-2009

Transcript of library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В....

Page 1: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ПУТЕЙ СООБЩЕНИЯ (МИИТ)

Институт управления и информационных технологий

Кафедра «Вычислительные системы и сети»

А. В. МИХАЙЛЮК

ВВЕДЕНИЕВ ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ

ПРОГРАММИРОВАНИЕ

Рекомендованоредакционно-издательским советом университета

в качестве учебного пособия для студентов, обучающихся по направлению

«Информатика и вычислительная техника»

МОСКВА-2009

Page 2: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

УДК 004 Мб 9

МихайлюкА.В. Введение в объектно-ориентированное программирование. Учебное пособие. - М.: МИИТ, 2009. - 340 с.

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

Примеры, приведенные в тексте и оформленные, как законченные проекты, можно скачать по адресу htfe://www.cisco acad.ru/mihailuk/oop.zip

Учебное пособие предназначено для студентов, обучающихся по направлению «Информатика и выч и ели тельная техника».

Рецензенты:

Вице-президент по разработке программных продуктов компании NetCracker, к.т.н. Файнберг М.А.

Заведующий кафедрой “Математического обеспечения авто матизиро ван ных систем управления» (МИИТ) д.т.н. профессор Доении В.В.

О Московский государственный университет путей сообщения (МИИТ), 2009

Page 3: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

СодержаниеП Р Е Д В А Р И Т Е Л Ь Н Ы Е С О Г Л А Ш Е Н И Я ................................................................................................. - б -

Б Л А Г О Д А Р Н О С Т И ............................................................................................................................................ - 7 -

В В ЕД Е Н И Е. Ц Е Л Ь И М Е Т О Д ...................................................................................................................... - 8 -

ГЛ А ВА 1. П Р О Г Р А М М И Р О В А Н И Е -П О С Т Р О Е Н И Е И Н Ф О Р М А Ц И О Н Н Ы Х М О Д Е Л ЕЙ Р Е А Л Ь Н О Г О М И РА ............................................................ .............................................................................. 11 -

ГЛ А ВА 2. О Б Ъ Е К Т Ы - К И Р П И Ч И , И З К О Т О РЫ Х П Р О Г Р А М М И С Т С Т Р О И Т СВО Й М И Р .............................................................................................................................. - .......................... - ............- 1 8 -

КЛАССЫ, КАК СРЕДСТВО ОПИСАНИЯ ОБЪЕКТОВ........................................................................................- 1 8 -Зам ечания о понятности п ро г ра м м ........................................................................... ......................... - 1 9 -ПОРЯДОК ЗАПИСИ КОММЕНТАРИЕВ.............................................................................................................. -21 -ПОРЯДОК ЗАПУСКА ИНТЕГРИРОВАННОЙ СРЕДЫ РАЗРАБОТКИ ПРОГРАММ НА ЯЗЫКЕ JAVA........... - 22 -С оздание нового п р о е к т а ........................................................................................................................ - 2 8 -О КЛЮЧЕВЫХ СЛОВАХ...................... .................................................... .........................................................-3 2 -О кон стру кто ра х .............................. .............................................................................................................- 3 2 -СПЕЦИФИКАТОРЫ ВИДИМОСТИ............................................................... .................. ....................................-3 5 -О бъекты - экзем пляры реализации к л а с с о в ................................................................................... - 3 6 -Пу сто е зн а ч е н и е ...........................................................................................................................................- 38 -П рим итивные типы д а н н ы х ..................................................................................................................... - 38 -

Числовые типы данных........................................................................... .......................................................................................................- 40 -Символьный тип данных ....................................................................................................................................................... ............................- 41 -Логический тип данных .......................................................................................................................................................................................-41 -

П рим итивны е и ссы лочны е т и п ы ................................. - 4 1 -В ы в о д ы .............................................................................................................................................................. - 4 2 -

ГЛ А В А 3. О П И С А Н И Е П О В Е Д Е Н И Я О Б Ъ Е К Т О В . С В О Й С Т В А И М Е Т О Д Ы ................ - 43 -

М етоды - способ описания поведения объектов .............................. - 4 3 -Метод - последователыюсть действий, описывающих поведение объектов...............................-4 3 -Порядок запуска программ на исполнение............................................... ....................................................................................- 44 -

В ы в о д ........................................................................... ......................................... ............................................ - 5 1 -ОПЕРАТ0Р ПРИСВАИВАНИЯ - ЕДИНСТВЕННЫЙ СПОСОБ ИЗМЕНИТЬ СВОЙСТВО ОБЪЕКТА ИЛИЗНАЧЕНИЕ перем енной ..................................................................................................................................- 53 -

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

Условные выражения. Операции сравнения. Переменные и свойства логического типа иоперации над нш т...................................................................................................................................................................................................... -6 0 -Логические выражения.........................................................................................................................................................................................-6 4 -Формы условных операторов. Программные блоки................... ........................................................................................- 6 8 -Выводы ......................................................................................... ....................... ...................................................................................................... ..............- 88-

Повторное вы полнение однотипны х действий - единственны й спо соб обработки больш их объемов информ ации О ператоры цикла - средства оп исан ия повторяю щ ихся

действий объ екта ........................................................................................................... ...............................- 89 -Оператор цикла fo r ................................................................................................................................................................................................... -9 0 -Оператор цикла while ............................................................................................................................................................................................-9 7 -Оператор цикла do ... while ................. ......................................................................................................................... ............................-1 0 4 -

В ы зов м етодов - способ заставить объект действовать . Передача парам етров - один изспособов обмена информ ацией между объектам и ................................................................... -1 1 1 -

Способы вызова методов.............................................................................................................................................................................. -111 -Способы передачи параметров методам ................................................................................................................................. -1 1 6 -Ороли методов............................... ......................... . ......................................... .............. ..................................................................................... - 120-

ГЛ А ВА 4. П О С Т РО Е Н И Е М И РА , Н А С Е Л Е Н Н О Г О Б О Л Ь Ш И М Ч И С Л О М Р А ЗН О О Б Р А ЗН Ы Х О Б Ъ Е К Т О В - О С Н О В Н О Й П У Т Ь Р Е Ш Е Н И Я С О В РЕ М Е Н Н Ы Х З А Д А Ч .............................................................................................................................................. - ................ - 1 2 2 -

Page 4: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

МАССИВЫ - ЛИНЕЙНЫЕ МНОЖЕСТВА ОБЪЕКТОВ.................................................................................. -1 2 2 -ОСНОВНЫЕ ОПЕРАЦИИ НА МАССИВАХ. СОРТИРОВКА И ПОИСК.........................................................- 128 -

Сортировка ...................................................................................................................................................................................................................... -1 2 8 -Поиск .........................................................................................................................................................................................................................................- ЫЗ -

Варианты построения разнообразны х списков - отраж ение связей между объектамиРЕАЛЬНОГО МИРА. ........ -1 5 8 -

Очередь...................................................................................................................................................................................................................................-1 5 9 -Упорядоченная очередь ..................................................................................................................................................................................... - 186 -Стек............................................................................................................................................................................................................................................-1 7 0 -Списки, как средства представления множеств............................................................................................................-174 -Сортировка списков............................ ..................................................................................................................................................................- 182 -

Д еревья - отраж ение иерархической связи между объектам и реального м и р а ...........-193 -

Г ЛА ВА 5. О Ш И Б К И В П РО Г Р А М М Н Ы Х С И С Т Е М А Х И П У ТИ Б О Р Ь Б Ы С Н И М И - 209 -

Повторное использование кода - один из путей создания надежного программногопродукта.............................................................................................................................................................................................................................. -2 1 3 -Абстракцня - основной путь преодоления проблемы усложнении программных комплексов - 215-Объектпо-ориентированный подход современный способ борьбы с нарастанием сложности программного обеспечения....................................................................................................................... ................- 216 -

ГЛ А В А 6. П О В Т О Р Н О Е И С П О Л Ь ЗО В А Н И Е КО Д А - П Е Р В Ы Й М Е Т О Д Б О Р Ь Б Ы С О С Л О Ж Н О С Т Ь Ю ...............................................................................................................................................- 2 1 8 -

П орядок хранения, ком пиляции и исполнения програм м на язы ке Ja v a .................218 -Понятие пакета Подклю чение па кетов ...........................................................................................- 2 ) 9 -Па кеты , входящ ие в состав язы ка , внеш ние п а кеты .............................................................. ..-221 -

Управление вводом-выводом ........................................................................ ................................................................... ........................ - 222 -Класс FilelnputSlream ......................................................................................................................................................................................... - 226 -Класс FileOulpulSlream..................................................................................................................................................................................... -227 -Класс PrintlVriter....................................................................................................................................................................................................... - 227-

Собственны е пакеты ................................................................................................................................ -231 -В ы во д ы .................... -2 3 9 -

ГЛ А ВА 7. И Н К А П С У Л Я Ц И Я - С К Р Ы Т И Е С Л О Ж Н О С Т И Д Л Я П О В Т О РН О Г О И С П О Л Ь ЗО В А Н И Я ....................................................................................................................................... - 241 -

ГЛ А В А 8, Н А С Л ЕД О В А Н И Е - О Д И Н И З Н А И Б О Л Е Е Н А Д Е Ж Н Ы Х С П О С О Б О В П О В Т О Р Н О Г О И С П О Л Ь ЗО В А Н И Я К О Д А ..................................................................................... - 245 -

Необходим ость н а с л ед о в а н и я ...........................................................................................................- 2 4 5 -П равила построения н о вы х классов на базе сущ ес твую щ и х ............................................ - 247 -К ласс O bject - праро ди тель всех к л а с с о в ......................................................................................- 2 5 1 -П орядок присваивания значений объ екта м ................................................................................. -253 -П ерекры тие методов Д инам ический вы зов м е т о д о в ............................................................. -255 -

ГЛ А В А 9. А Б С Т Р А К Ц И Я - О Д И Н И З С П О С О Б О В О Т Л О Ж И Т Ь Р Е Ш Е Н И Е ЗА Д А ЧИ НА Б О Л Е Е П О ЗД Н И Е Э Т А П Ы ....................................................................................................................... - 259 -

А бстрактные классы и абстрактны е м етоды , как прием переноса реш ения о поведенииобъекта на более поздние стадии ра зра ботки ..............................................................................-2 5 9 -Интерфейсы , как более вы сокая ступень абстракции ............................................................. - 263 -Применение интерф ейсов для разработки универсальны х програм м ..............................- 268 -В ы в о д ..............................................................................................................................................................-2 7 4 -

ГЛ А ВА 10. П О Л И М О Р Ф И ЗМ - О Д И Н И З С П О С О Б О В Б О Р Ь Б Ы С О С Л О Ж Н О С Т Ь Ю ..... -2 7 6 -

П ерегруженность оп ераций ....................................................................................................................-2 7 6 -П ерегруж енность м етодо в . Понятие сигнатуры м етода .......................................................... -2 7 7 -

ГЛ А ВА I I . М Н О Г О П О Т О Ч Н О С Т Ь - С П О С О Б О Т Р А Ж Е Н И Я П А Р А Л Л Е Л Ь Н О РА ЗВ И В А Ю Щ И Х С Я П Р О Ц Е С С О В РЕА Л ЬН О ГО М И Р А ....................................................... - 282 -

Понятие потока С пособы описания и запуска пото ков ...................................................... -282 -

Page 5: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Поток главной программы ................................. ................................................................................................. ....................................... - 286 -Создание потока путем расширения класса Thread.............................................................. ..................... - - 287 -

( 'оздание потока путем реализации интерфейса Runnable ............................................................................. - 289 -Состояния ПОТОКОВ ......................................................................................................................... -291 -ПриориТеты потоков. Управление приоритетами............................ ..................................... -293 -Приостановка п о то к о в .................................................................................................. .................-297 уОбработка общих данных - неизбежная проблема при работе с потоками Понятиекритического ресурса Синхронизированные блоки............................................................- 301 -Синхронизированные методы, как один из приемов синхронизации работы потоков...... -зю-

Выводы ................................................................................................................................................................................................................................. - 313 -

Р еализация семафоров » важнейший прием регулирования доступа к критическимресурсам..................... .................................................................................................................. ....... -314 -

Синхронизирующие методы wailQ и notify!')..........................................................................................................................- 214 -

Простой двоичный семафор............. ......................................... ...............................................................................................................- 321 -

Целочисленные семафоры .................................................................................................................. ..........................................................- 323 -

В ыводы................................................................................... -336-

ЗАКЛЮЧЕНИЕ.......................................................................................................................... -337-

ЛИТЕРАГУРА................................................................- ...........................................................-339-

Page 6: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Предварительные соглашенияВсе изложение материала ведется с использованием

шрифта Times New Roman.Все тексты программ записаны шрифтом Courier New

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

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

Page 7: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

БлагодарностиДанная книга появилась в результате наших дискуссий

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

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

Особая благодарность моей супруге Ларисе, поддержавшей меня в этой работе и оказавшей огромную помощь в работе над текстом.

Page 8: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Введение. Цель и методПриступая к написанию этой книги, я ставлю перед

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

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

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

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

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

Page 9: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Как только мы заговорили о примерах, возникает вопрос, на каком языке программирования будут написаны эти примеры? В качестве основного и единственного языка программирования мы выберем язык Java, а в качестве среды исполнения программ выберем JBuilder 2005. Примем выбор языка программирования и среды разработки без развернутого обоснования сделанного выбора. Скажем только, что язык Java с момента своего изобретения был строго объектно-ориентированным. Сейчас сложилось устойчивое сообщество программистов, работающих на этом языке, и можно с большой долей уверенности утверждать, что в ближайшие несколько лет это язык будет оставаться одним из флагманов современного программирования. Интегрированная среда разработки JBuilder 2005 принята в качестве основной для языка Java на кафедре Вычислительных систем и сетей. Данная книга в первую очередь адресована студентам, обучающимся на этой кафедре. Так что о выборе среды программирования можно почти и не говорить. Однако данная книга - не учебник по языку Java, мы не ставим своей целью дать полное описание языка программирования. Это книга, которая поможет своим читателям освоить основы программирования.

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

Page 10: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Page 11: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 1. Программирование - построение информационных моделей реального мира

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

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

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

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

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

Page 12: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

Page 13: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Сведем установленные нами сущности вместе с основными свойствами в таблицу.

Таблица 1.

Адвокат

1 Фамилия Имя Отчество

Идентификация адвоката в бюро

2 Номер счета в банке Для построения перевода денег на счет адвоката

Действия

1 Построить таймшит

2 Просмотретьдоходы

Клиент

1 Наименованиеорганизации

Для идентификации клиента и формирования счета

2 Адрес Для отправки счета

Действия

Page 14: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

1 Оплатить смета

Договор

1 Указание на клиента

Для привязки договора к клиенту

2 Дата и номер договора

Для однозначной идентификации договора

3 Способ оплаты В рассматриваемом случае оплат происходит повременно, но возможны разные варианты оплаты услуг адвоката

4 Ставка адвоката (создается для каждого адвоката, работающего с данным клиентом)

Используется при расчете суммы гонорара адвоката

Таймшит

1 Период (год и месяц)

Для привязки к определенному счету

2 Ссылка на адвоката Для привязки определенному адвокату

3 Ссылка на клиента Для привязки к определенному клиенту

4 Дата выполненной работы

Для формирования отчета

5 Описание работы Для формирования отчета

6 Затраченное время Для формирования отчета и для расчета

Page 15: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

гонорара

Действия

1 Установить время начала работы

2 Установит время окончания работы

Отчет

1 Ссылка на период Для привязки к таймшитам,сформированным в этот период

2 Ссылка на клиента Для привязки к клиенту

3 Строки отчета:

Адвокат

Дата выполнения работу

Описание работы

Затраченное время

Ставка адвоката

Сумма адвоката за выполненную работу

4 Итоговая строка отчета: общая сумма по всем адвокатам, работавшим на данного клиента в данный период

Для формирования счета

Page 16: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

времени

Действия

1 Создать отчет

Счет

1 Номер счета Для однозначной идентификации счета

1 Адрес клиента Для доставки счета

2 Ссылка на отчет Для обоснования выставленной к оплате суммы

3 Сумма счета

4 Адрес банка и счет адвокатского бюро в банке

Куда плательщику переводить деньги

1 Создать счет

Адвокатское бюро

1 Адрес банка и счет в банке

Для формирования счетов

Бухгалтер

1 Фамилия, имя и отчество

Идентификация в системе

Действия

1 Создать счет

Page 17: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

2 Принять платеж

3 Распределить доходы между адвокатами

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

Программист в ходе своей профессиональной деятельности создает информационную модель моделируемого процесса.

Page 18: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 2. Объекты - кирпичи, из которых программист строит свой мир

Классы, как средство описания объектов.

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

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

Page 19: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Замечания о понятности программНа количество символов в именах Java не накладывает

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

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

Page 20: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

Page 21: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

комментирование программы требует от программиста некоторых усилий и хорошего вкуса.

Порядок записи комментариевЯзык Java предполагает три вида комментариев.1- й вид - однострочные комментарии. Как уже было

сказано выше, текст программы записывается в обычный текстовый файл. Текстовый файл состоит из строк. Если мы в любой строке с любой позиции запишем подряд два слеша (//), то все, что будет записано после этих знаков до конца строки, будет комментарием. Однострочные комментарии удобны для кратких пояснений.

2- й вид - многострочные комментарии. Такие комментарии начинаются символами /* и заканчиваются символами */. Они могут занимать любое число строчек программы. Многострочные комментарии удобны при написании развернутых пояснений к программе.

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

Page 22: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Порядок запуска интегрированной среды разработки программ на языке Java

Для точного совпадения всех описываемых действий с теми, которые Вы должны выполнить на своем компьютере, на нем должен быть установлен пакет “JBuilder 2005 Foundation”. Вы можете использовать любой другой пакет, но точного совпадения с интерфейсом, описываемым в книге, не будет. Программы же должны исполняться одинаково во всех средах.

Из главного меню компьютера выберите последовательность:

«Пуск / Программы / JBuilder 2005 Foundation / JBuilder 2005 Foundation»

После запуска интегрированной среды разработки JBuilder Вы получите на экране примерно картинку, показанную на рис. 1.

Page 23: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 1. Вид экрана после запуска интегрированной среды программирования JBuilder 2005 Foundation.

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

Page 24: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

исполните ‘'File / Close Project../'. После чего Вы получите диалоговое окно, показанное на рис. 2.

iCjrGose Projects--------*------*■—

щ f—i feV Welcome jpx

fxtиимС.

t

Рис. 2. Окно закрытия проектовПоследовательно нажмите кнопки АП и ОК. Проект

Welcome закроется, и Вы увидите чистый экран, который показан на рис. 3.

Page 25: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 3. Вид экрана пред началом работы.Вся работа в среде JBuilder ведется над проектами. В

проект включаются все файлы, связанные с решением определенной задачи. Для проекта строится специальная директория. Имя директории совпадает с именем проекта. В этой директории располагается файл, описывающий проект. Он имеет имя, совпадающее с именем проекта и расширение .jpx. Кроме того, в состав директории проекта входят другие директории. В частности директория, в которой хранятся все

Page 26: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

исходные файлы проекта. Она, как правило, имеет имя src. И директория с результатами трансляции исходных текстов. Она, как правило, имеет имя classes.

Для настройки интегрированной среды на работу в определенных директориях необходимо провести предварительную настройку ИНтегоированной средыJBuilder на работу в определенных директориях. Для этого из главного меню выполните последовательность команд “Project / Default Project Properties...”. В ответ откроется диалоговое окно, показанное на рис. 4.

Page 27: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 4. Вид диалогового окна проектов по умолчанию.

настройки свойств

Page 28: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Предположим, что мы будем располагать все проекты в директориях, расположенных на дискеC:\users\Baaaeaen\document.s\My eBooks\capterXX, где XX - номер главы. В этом случае настройте все директории, показанные на рис. 4 так, чтобы они показывали на нужные нам директории.

Создание нового проектаИз главного меню исполните команду “File / New

Project...”. В открывшемся окне заполните поля Name и Directotry значениями “Lawyers” и“С: /U sers/Вл адел ец/Docu m en ts/MyeBooks/Chapter02/Lawyers”, соответственно. После заполнения Диалоговое окно Project Wizard будет выглядеть, как показано на рис. 5.

Рис. 5. Заполнение параметров проекта информацией об имени проекта и расположении проекта.

Остальные параметры создаваемого проекта нас устраивают по умолчанию, потому нажмите клавишу Finish. Новый проект Lawyers в директории Chapter02 будет построен.

Page 29: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

В окне управления проектом, которое называется Project, появится информация о проекте. Станьте мышкой на <Project Source> и вызовите контекстное меню правой кнопкой мышки. Исполните из него последовательность команд “New / Class...” как показано на рис. 6.C i JBiJilde: ?ОПО С Afcei^/Bivwi«i/DiKa.»riefes/My ijBooksAJ>ajder02/Lawyere/tjawyeis ft»

: File Search Pefacie^ , Ftotec* Rtm Team Enterprise ^opfs

* ' s ~

a1 • " . t -r 3 я

Рис. 6. Вызов мастера создания нового классаВ ответ на эту команду получите мастер создания

классов, показанный на рис. 7. Заполните имя класса, остальные параметры нас устраивают значениями по умолчанию.

Page 30: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 7. Заполнение параметров мастера построения классов.

Нажав кнопку ОК, получите в окне редактирования исходных текстов готовое описание класса Lawyer, соответствующее сущности «Адвокат», описанной в 1-й главе.

Текст автоматически построенного класса Lawyer приведен ниже:/*** <p>Title: </р>

* <p>Descript.ion: </р>

* <p>Copyright: Copyright (с) 2009</р>

* <p>Company: </р>** Sauthor not attributable* @version 1.0 * /

public class Lawyer ■ public Lawyer () {

Page 31: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Вступительный комментарий, автоматически построенный мастером построения классов, является документирующим комментарием, что видно из его открывающих символов. Кроме заголовков основных разделов, которые должны присутствовать в документирующих комментариях он содержит конструкции <р> и </р>. Эти записи представляют собой фрагменты описания страниц, предназначенных для отобрадения в интернете. Это так называемые теги, выделяющие в текстах смысловые единицы. Впишем в него соответствующую смыслу задачи информацию. В дальнейшем этот комментарий ляжет в основу документации по нашему проекту. После заполнения вступительного комментария наш текст будет выглядеть следующим образом:/ * ** ■* <p>Title: Адвокзт</р>

* <p>Description: -Kim<ic; Lawyer предназначен* для моделирования■действий адвоката* по созданию таймшитов </р>** <p>Copyright: Copyright (с) 2009</р>** <p>Company: МИИТ </р>

* @aut.hor Михайлкж А, В.* 0 ve г s i on 1.0 */

public class Lawyer! public Lawyer () {

' }}

Рассмотрим, из чего состоит описание класса. Оно состоит из двух частей: заголовка и тела. Заголовок public class Lawyer состоит из трех слов. Ключевое слово public называется спецификатором видимости класса. Ключевое слово class говорит о том, что мы имеем дело с описанием класса. Идентификатор Lawyer - придуманное нами имя класса. В заголовке класса могут стоять другие спецификаторы. Их роль, место и назначение мы

Page 32: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

рассмотрим по мере продвижения в изучении языка программирования.

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

• Описания свойств• Описания конструкторов• Описания методовОписание свойств задает набор свойств объектов

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

О ключевых словахВ конце предыдущего раздела используется

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

О конструкторахВыше упоминалось понятие конструктора.

Конструктор -- это специальное средство языка,

Page 33: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

public Lawyer () {)

Он представляет собой простейший конструктор, который был построен мастером создания классов потому, что мы оставили взведенным флажок «Generate default constructor». Конструктором по умолчанию является конструктор с пустым списком параметров и пустым телом.

Повторим описанные выше шаги для построения остальных классов. Построим остальные классы.

Имена классов и их соответствие сущностям, описанным выше в настоящем разделе

Таблица 2.

№п./п.

Наименованиесущности

Имя класса

1 Адвокат Lawyer

2 Клиент Client

3 Бухгалтер Bookkeeper

4 Адвокатскоебюро

Office

5 Договор Contract

Page 34: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

6 Таймшит Timesheet

7 Отчет Report

8 Счет Invoice

9 Платежноепоручение

Payment

Если Вы самостоятельно построите все указанные в таблице 2 классы, то точно научитесь создавать новые классы в среде JBuilder. Вид проекта после создания основных классов показан на рис. 8.

Рис. 8. Вид проекта после создания основных классов.Важно пояснить, что мы видим на экране. В левом

верхнем окне представлены в виде дерева все созданные нами классы, входящие в наш проект Lawyers. В левом нижнем окне представлена структура того класса, который в настоящее время отображен в окне редактирования. Окно

Page 35: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Спецификаторы видимостиСпецификаторы видимости указывают пространство, в

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

В языке существуют 4 варианта указания видимости объектов, свойств и методов:

1- й вариант - p r iv a t e . Свойства и методы, описанные как приватные доступны для использования только в том классе, в котором они описаны. Общая методическая рекомендация состоит в том, что все свойства объектов надо делать приватными, а доступ к ним со стороны других объектов предоставлять через специальные методы. Такой способ описания свойств позволяет защитить свойства объектов от некорректного использования.

2- й вариант - p r o te c te d . Свойства и методы, описанные как защищенные, доступны в том кпассе. котором они описаны и в тех классах, которые построены на основе этого масса. Мы пока не говорили о том, как строить новые классы на основе существующих. Вернемся к этому спецификатору, когда будем обсуждать построение новых классов.

3- й вариант - p u b lic . Свойства и методы, описанные как публичные, доступны для использования из других классов и методов. Свойства и методы, описанные как

Page 36: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public, предназначены для связи объектов с внешним по отношению к ним миром.

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

Объекты - экземпляры реализации классов

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

Для этого есть специальная языковая конструкция, которая выгладит следующим образом:new <имя класса>([<параметры>]);

Например, для создания нового объекта класса Lawyer надо записать:new Lawyer () ;

Для создания нового объекта класса Client:new Client ();

И так далее.Однако от такого создания объектов может быть не

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

Page 37: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Рассмотрим несколько простых примеров.lawyer ivanov;

Описывается переменная ivanov предназначенная для хранения связи с объектом типа Lawyer. После этого описания можно пользоваться идентификатором ivanov для ссылки на объект класса Lawyer.Lawyer ivanov, petrov;

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

Page 38: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

«адвокат Иванов», а на языке Java “Lawyer ivanov”. Не надо удивляться: другой язык - другие правила написания. Причем это не жесткое требование языка, а устоявшаяся традиция программирования, которой мы будем следовать. Описанные имена предназначены для того, чтобы указывать на объекты класса Lawer. В данном случае мы всречаемся с перменными или свойствами ссылочного типа. Такие имена предназначены для указания на объекты соответствующего класса. Возможна и следующая записьLawyer ivanov = new Lawyer О;

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

Возможно и такое описание:Lawyer ivanov = new Lawyer M, petrov, sidorov = new

Lawyer ();В этом примере вводится три переменных или свойства

ivanov, petrov и sidorov класса Lawyer, переменные ivanov и sidorov получают значения объектов, а переменная petrov имеет пустое значение, которое обозначается ключевым словом n u l l .

Пустое значениеЗначение null называется пустым значением. Оно

говорит нам, что переменная или свойство, ни с каким объектом не связана.

Примитивные типы данныхМы уже познакомились с основными «кирпичами», из

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

Page 39: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Примитивные типы данных, предопределенные в языке Java сведены в таблицу 3.

Таблица 3.

Описание типа Назва­ние

Дли­на Диапазон значений

byte 1 О т-128 до 127

short 2 От -32768 до 32767

Целые int 4 О т-2147483648 до 2147483647

Число­вые

long 8 От -9223372036854775808 До 9223372036854775807

Плава- float 4 От 3.4е-38 до 3.4е+38ющие double 8 От 1.7е-308 до 1.7е308

Симво­льные

char 2 От 0 до 65535

Логические boolean 1 false true

Page 40: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Числовые типы данныхЧисловые типы делятся на целые, плавающие и

символьные.Целые числа записываются в дополнительном коде,

поэтому в отрицательную сторону они имеют диапазон на единицу больше, чем в положительную. Целые числа записываются как последовательность цифр, перед которой может стоять знак + или -. Примеры правильной записи целых чисел приведены нижеint х = 25; int у = -72;

Плавающие числа предназначены для представления чисел имеющих целую и дробную части. Во внутреннем представлении плавающие числа организованы в виде пары: мантисса и прядок в соответствии со стандартом IEEE-754. Плавающие числа чаще всего используются в научных расчетах. В записи плавающих чисел могут быть представлены целая и дробная части. Целая часть отделяется от дробной точкой. В представлении числа может использоваться экспонента с основанием 10, представленная в виде буквы е или Е, после которой в виде целого числа представлена степень 10.

Примеры правильной записи чисел с плавающей точкой приведены ниже с пояснениями в форме комментариев.float alpha = 25; float beta = 25. ; float gamma = 25.0;float delta = +25.0; // все 4 строки представляют одно

// и то же число// варианты записи чисел, имеющих дробную частьfloat alpha = 25.75;float alpha = 0.25;float alpha = .25;float alpha = -.25;// варианты записи чисел с указанием экспоненты float alpha = 250е0; float alpha -- .25еЗ;float alpha = 2500e-l; // последние 3 строки

//представляют число 250

Page 41: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Символьный тип данныхСимвольные переменные имеют двоякую природу. Их

основное предназначение - представлять отдельные литеры в различных алфавитах. Например, буквы русского алфавита от а до я, заглавные буквы от А до Я, буквы латинского алфавита, знаки препинания, знаки операций, представленные на стандартной клавиатуре компьютера. В языке принята одна кодировка символов так называемый юникод, когда каждый символ кодируется в два байта. С другой стороны символьный тип относится к числовому типу данных. С каждым символом сопоставляется его числовой код в кодировке юникод и над символьными данными можно производить арифметические операции, как над целыми числами. Ниже приводятся примеры описания символьных данныхchar а = 'а'; char yaRus = 'я'; char plus = ;

Логический тип данныхЛогические переменные стоят несколько особняком.

Они предназначены для хранения логических значений ИСТИНА (true) и ЛОЖЬ (false). Для хранения каждой логической переменной отводится один байт памяти. Ниже приводятся примеры правильного описания логических или как их еще называют булевых переменных:boolean flag = true; boolean isEmpty = false;

Примитивные и ссылочные типыЯзык Java относится к сильно типизированным языкам

программирования. Это означает, что все используемые в программе имена должны быть описаны. В момент описания имя связывается с типом. Тип не может быть изменен в течение времени жизни имени. Имя - хранитель значения. Тип значения зависит от типа, связанного с именем.

Page 42: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

ВыводыДля описания объектов используются классы. Классы -

это статические описания объектовОписание класса состоит из заголовка и тела. В

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

Для создания объектов применяются специальные языковые единицы, называемые конструкторами.

Свойства входят в состав классов и представляют собой имена, описанные в теле класса.

Переменные входят в состав методов и представляют собой имена, описанные в теле метода.

В тексте программы имена обозначаются идентификаторами. Имена предназначены для связи со значениями

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

К примитивным типам относятся int, short, byte, long, float, double, char, boolean. Остальные типы являются ссылочными.

Если имя имеет примитивный тип, то оно напрямую связывается со своим значением.

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

Несколько имен могут иметь ссылку на один и тот же объект.

Page 43: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 3. Описание поведения объектов. Свойства и методы

Методы - способ описания поведения объектов.Метод - последовательность действий, описывающих поведение объектов.

Итак, мы переходим к рассмотрению того, что представляют собой методы. Методы - это способы описания поведения объектов. Собственно все искусство программирования заключается в умении написания методов. В общем случае методы имеют следующий вид:«спецификаторы метода> «Имя метода> ([«параметры>]){

[«описания переменных?-][«последовательность операторов?]

}Из всех возможных спецификаторов методов нам пока

известны спецификаторы видимости private, protected и public. Этих спецификаторов хватит для начального этапа работы, новые будем вводить в дальнейшем, когда понадобится. Кроме спецификатора видимости надо обязательно указать тип значения, вырабатываемого данным методом. В качестве типа можно указать любой из уже известных нам примитивных типов или имя класса. В том случае, если метод не вырабатывает никакого значения, то в качестве типа возвращаемого значения указывается ключевое слово void.

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

Page 44: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

Порядок запуска программ на исполнениеДля демонстрации последовательного исполнения

операторов языка создадим новый проект, который назовем Sequence. Прядок создания проекта уже обсуждали выше и повторяться не будем. Скажем только, что этот проект создается в директории Chapter03.

После успешного создания проекта запустим мастер создания класса, как в предыдущем примере, но установим по-другому флажки. Взведем флажок «Generate main method» для создания метода main. И сбросим флажок «Generate default constructor». Нам в данном примере

Page 45: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Рис. 10. Мастер создания классов для проекта Sequence.Нажав кнопку ОК, дописав необходимые комментарии

и три строчки текста программы, получим следующий текст:/**

* <p>Title: Sequence demo</p>•к* <p>Description: Простейшая программа,1 демонстрирующая принцип последовательного* исполнения операторов</р>

* <p>Copyr.ight: Copyright (с) 2009</р>

* <р>Сошрапу: МШТ</р>** ^author Михайлюк А.В.* Aversion 1.0 */

public class SequenceDemo {public static void main(String[J args) (

System.out.println("Исполняется перзым");System, out. print. In ( "Исполняется вторым") ;System.out.print 1n ("Исполняется третьим");

Page 46: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

})

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

Строка public class SequenceDemo является заголовком класса. Она начинается с двух ключевых слов public - спецификатор видимости и class - указание на то, что дальше пойдет описание класса. SequenceDemo -придуманное нами имя класса. В соответствии с традицией языка имя класса пишется с заглавной буквы.

Тело класса начинается с открывающейся фигурной скобки. В теле класса описан один метод. Описание метода начинается с заголовка public static void main(String[] args). Особенностью описываемого метода является то, что он должен быть запущен исполнительной системой как главный метод, который запускается первым. Спецификатор видимости public нужен, чтобы запускаемый метод был доступен исполнительной системе извне классаSequenceDemo.

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

Page 47: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

а это как раз то, что и нужно. Объектов еще нет, а метод запустить уже можно.

Спецификатор типа void говорит о том, что метод не возвращает никакого значения.

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

Переходим к рассмотрению конструкции (String [] args). В круглых скобках, как уже говорилось выше, указываются параметры запуска метода. В данном случае список состоит из одного элемента. Этот элемент является массивом строк. Спецификатор типа элементов массива String - имя предопределенного в языке класса String, предназначенного для хранения строковых значений. Ниже будем знакомиться с предопределенными в языке классами. Сейчас же скажем, что это класс, специально предназначенный для хранения строковых значений. Теперь о массивах. Массивы - это тоже предопределенный в языке класс, предназначенный для хранения однотипных данных. Идентификатор args - имя параметра. Это единственный идентификатор в этой строке, который может быть безболезненно изменен программистом на любой другой. Для понимания программы важно понять, что список параметров главной процедуры задается при запуске программы в виде одной строки символов. В этой строке параметры записываются подряд и разделяются пробелами. Исполнительная система преобразует эту строку в массив строк, используя пробелы в качестве разделителей элементов массива.

Тело метода состоит из трех однотипных строк:System.out.println("Исполняется первым");

Page 48: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System, out .println ( "Исполняется вторым");System.out.println("Исполняется третьим");Каждая строка - один оператор. Оператор в данном

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

Осуществим компиляцию и запуск нашей программы. Для компиляции программы из главного меню исполним команды Project / Make project “Sequence.jpx”. Проект пройдет компиляцию, после чего будет готов к исполнению. Если в исходном тексте будут обнаружены ошибки, то информация о них будет выведена на консоль исполнительной системы в нижней части экрана. Их надлежит исправить и повторить компиляцию проекта.

Для запуска программы нажмем клавишу F9 или выполним из главного меню команды Run / Run Project. В ответ на это мы получим диалоговое окно, которое просит нас указать все необходимые параметры запуска нашей программы. Если эти условия укажем правильно, то при последующих нажатиях F9 это окно высвечиваться не будет, а будет сразу запускаться программа с теми же условиями, что и в предыдущий раз.

Page 49: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 11. Окно заполнения параметров запуска программы.

Все параметры запуска нас устраивают, только поле Main class не заполнено. Для его заполнения нажмем кнопку (...) справа. В ответ на это получим диалоговое окно выбора главного класса, показанное на рис. 12.

Page 50: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 12. Окно выбора главного класса проекта.Отыщем в предлагаемом дереве доступных нам классов

главный класс нашего проекта. В данном случае он последний в списке. Выберем его двойным кликом. Получим окно, аналогичное показанному на рис. 11, но с заполненным полем Main class.

Page 51: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

[ f# New Rmtime Configuration Ш

Рис. 13. Окно параметров запуска программы после выбора основного класса.

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

После нажатия кнопки ОК программа скомпилируется и выполнится. На консоли вывода исполнительной системы мы получим строки - результат работы нашей программы:Исполняется первым Исполняется вторым Исполняется третьим

Этот текст соответствует тому, который мы поместили в поле параметров процедуры println.

ВыводОператоры программы исполняются последовательно.

Page 52: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Поведение объектов описывается в методах.Каждый метод состоит из заголовка тела.Заголовок содержит спецификаторы метода,

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

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

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

Page 53: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Оператор присваивания - единственный способ изменить свойство объекта или значение переменной.

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

Он имеет следующую структуру:•«переменная или свойство;» = <иь:ражение>;

Признаком оператора присваивания является знак =. Слева от него стоит переменная или свойство, которым присваивается значение выражения, стоящего справа.

Простейшие операторы присваивания могут выглядеть следующим образом:int X, у, 2 ',х = У ; у =■ х + \ ;

2. ~ х * у;После выполнения этих операторов переменная х будет

равна 2, у будет равна 3, переменная z будет равна 6.Несмотря на свою простоту, существует огромное

множество вариантов записи оператора присваивания.В зависимости от входящих в выражение операндов и

операций они будут иметь разный тип.

Арифметические выражения.Они строятся из числовых операндов и следующих

операций

Опера- Название Пример1

значениеция |

+ сложение 2 + 3 j 5

Page 54: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

вычитание 2 - 3 -1* Умножение 2 * 3 6

! Деление 3 /2 I

3 /2 .0 1.51% Взятие остатка

отделения l-ro операнда на 2- й

10% 5 0

12% 5 2

Приоритет операцийОперации исполняются слева направо, операции

умножения, деления и взятия остатка выполняются раньше операций сложения и вычитания. Например, выражение2 + 3 * 4

даст результат 14.Для изменения порядка выполнения операций

применяются круглые скобки. Операции в круглых скобках выполняются раньше, чем вне их. Так выражение( 2 + 3) * 4

даст результат 20.

Приведение типов при выполнении операцийЕсли в выражении присутствуют переменные типа byte,

short, char, то они предварительно будут расширены до целого типа, после чего над ними будет произведена арифметическая операция.

Так фрагмент программыbyte х = 0, у; у = х + 1;

даст ошибку присваивания, поскольку при вычислении выражения х + 1 число «1» типа int, а присваивать переменной типа byte значение типа int нельзя.

Page 55: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Если в выражении присутствует переменная типа long, то переменные типа byte, short, char и int будут предварительно расширены до 8 байт, после чего над ним буду выполнены операции.

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

Если в выражении присутствует операнд типа double, то другие операнды будут приведены к типу double, после чего над ним будут проведены вычисления.

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

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

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

Page 56: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Таблица 4

Типы, к которым осуществляется приведение

byte short char int long float double

byte N A H A A A A

short H N H A A A A

char H H N A A A A

int H H H N A A A

long H H H H N A A

float H H H H H N A

double H H H H H H N

Пояснения к таблице 4.А - автоматическое приведение. Н - ручное

приведение. N - приведение не требуется.Ручное приведение означает, что язык допускает это

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

Явное приведение типов обозначается в Java указанием в скобках того типа, к которому надо провести преобразование.

Так в приведенном выше примере решением будетbyte х = 0, у; у = (byte) (х + 1) ;

Сокращенная запись операторов присваиванияВ практике программирования часто встречаются

конструкции, когда требуется, увеличить значение какой-

Page 57: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Так следующую последовательность операторовint. X 0, ух •- X + У'X = X - у ;X = X * у;X = X / у;

можно заменить следующей эквивалентной:int х = 0, у - 2; х = f у ; х — у; х = * у; х =/ у;

Одноместные операторы изменения знака, инкремента и декремента

Кроме обычных двуместных операций в языке предусмотрено несколько одноместных операций. То есть операций, имеющих один операнд. Простейшая одноместная операция - изменение знака числа. Она будет выглядеть следующим образом:int х = 5, у; у - -х;

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

В арифметических выражениях используются еще две одноместные операции инкремента (++) и декремент (—). Они имеют очень простой смысл. Их можно считать сокращением операции присваивания и приращения (инкремент) или уменьшения (декремент) на единицу.

Можно написать:int х = 0, у = 0; х = х + 1;у = у - 1;

или

Page 58: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

int x = 0, у = О;х++;у--;

В обоих случаях после выполнения последовательности операторов х = 1, у = -1.

Но в использовании этих операций есть тонкость, о которой надо помнить при их использовании в выражениях. Обе операции имеют префиксную форму (++х) и постфиксную форму (х++). Обе они дают одинаковый результат при присваивании новых значений переменным, к которым они относятся, он сам результат выражения ++х и х++ будет разный. Префиксная операция сначала меняет значение выражения, постфиксная сначала выдает результат, а потом меняет значение выражения. Для демонстрации этих особенностей операций ++ и - - в обеих формах, можно посмотреть проект IncrementDecrement в директории СЬарегОЗ / * *** <p>Title: Инкремент Декремент</р>★* <p>Description: Демонстрация особенностей использования* операций ++ и --</р>** <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р>** ©author not attributable* ©version 1.0* /

public class IncDecDemo {public static void main (String[] args) {

int x ■= 0, у = 0, z = 0, w = 0;System.out.println("До выполнения операций:");System.out.println("x = " + x + " у = " + у

+ " z = " + z + " w = " + w) ;System.out.println("В момент выполнения операций:"); System.out.println("х++ = " + x++ + " ++y = " + ++y

+ " z-- = " + z-- + " --w = " + --w);System.out.println("После выполнения операций:"); System, out. println ("x = " ■+ x ч " у = " + у

+ " z = " + z + " w = " + w) ;)

l

Page 59: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Результатом выполнения этой программы будет следующий текст:До выполнения операций:

x = 0 y - 0 z = 0 w = 0 В момент выполнения операций: х + + - 0 ++у = 1 z — = 0 — w = -1 После выполнения операций: x = l y = l z = - l w = - l

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

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

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

Но не все в жизни так последовательно. Мы постоянно решаем задачи выбора. Если хорошая погода, то мы решим дойти от работы до метро пешком, если плохая, то, скорее всего, постараемся добраться на транспорте. Аналогично мы каждый день решаем задачу, как нам одеваться в зависимости от погоды и цели нашего выхода из дома. Одно дело сходить в ближайший магазин за продуктами, и совсем другое пойти на премьеру в Большой театр. Ясно, что мы

Page 60: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

будем принимать решение в зависимости от множества условий.

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

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

Условные выражения. Операции сравнения. Переменные и свойства логического типа и операции над ними.

Основным механизмом, реализующим в программировании выполнение различных действий в зависимости от условий, является условный оператор. Его общая форма имеет следующий вид:if (<условное выражение;») <оператор1>

ИЛИ

if («условное выражение;-) <оператор1> else <оператор2>

Первую форму оператора if следует понимать так. Вычисляется <условное выражением Если оно имеетзначение «истина», то выполняется <оператор1> и после этого начинается исполнение оператора, следующего за оператором if. В противном случае никаких действий не выполняется и начинается исполнение следующего за оператором оператора.

Вторую форму оператора if следует понимать так. Вычисляется <условное выражением Если оно имеетзначение «истина», то выполняется <оператор1>, в противном случае исполняется <оператор2>. После чего в обоих случаях управление предается оператору, следующему за оператором if.

Прежде всего, следует разобраться с конструкцией (<условное выражение>). Условное выражение - это выражение, которое может давать одно из двух значений

Page 61: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

«истина» и «ложь», в языке Java эти два значения обозначаются true и false.

Нам уже знаком примитивный тип данных boolean. Переменные и свойства типа boolean могут иметь значения true и false, следовательно, они могут выступать в роли условных выражений в операторе if.

Кроме того, в языке определены, так называемые, операции сравнения. Эти операции применяются к парам арифметических выражений и вырабатывают значение true, если условие сравнения выполнено и false в противном случае.

Операции сравнения приведены в таблице 5. Таблица 5.

№п/п

Операция Пояснения Пример

1Равенство. Дает true, кода значение выражения слева от знака равенства равно значению выражения справа от знака равенства. В противном случае дает false

2!= Неравенство. Дает true, кода

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

3 > Больше. Дает true, кода значение выражения слева от знака равенства больше значения выражения справа от знака равенства. В противном случае дает false

Page 62: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

4>= Больше или равно. Дает true,

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

5 < Меньше. Дает true, кода значение выражения слева от знака равенства меньше значения выражения справа от знака равенства. В противном случае дает false

6 <= Меньше или равно. Дает true, кода значение выражения слева от знака равенства меньше лил равно значению выражения справа от знака равенства. В противном случае дает false

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

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

Page 63: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

/*** <p>Title: </p>** <p>Description: </p>** <p>Copyr.ight: Copyright (c) 2009</p>** <p>Company: </p>4r* ^author not attributable* 0version 1.0 * /

public class BoolenDemo {public static void main(String[] args) {

boolean flag] = true, fiag2 =• false;System.out.println("Значения булевых переменных:"); System.out.println("flagl = " + flag!

+ "; flag2 = " + flag2); int x = 1, у = 5, z = 2;System.out.println("Определены следующие"

-t " переменные:");System.out.println("x = " t x + "; у = " + у

+ "; z = " + z) ;

}

System.out.println("Результаты операций сравнения:System.out.println ("x == у " + (x == y)

+ "; у — z " + (у == z));System.out.println("x !- у " + (x !~ y)

+ "; у ! = z " + (у ! = ? . ) ) ;

System.out.println("x > у " + (x > y)+ "; у > z " + (y>z));

System. out. println ( "x >= у " + (x >-= y)+ "; у >= z " + (y >= z));

System.out.print 1n ("x < у " + (x < y)+ "; у < z " + !y<z));

System, out. println ( ”x <= у " + (x <=• y)+ "; у <= 2. " + !y <= z) ) ;

);

После запуска программы мы получим на консоли вывода следующие результаты:Значения булевых переменных:

flagl •= true; flag2 = false Определены следующие переменные: х = 1; у = 2; z = 2 Результаты операций сравнения: х == у false; у == z true х != у true; у != z false х > у false; у > г false

Page 64: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

х >= у false; у >= z true x < у true; у < z false x <= у true; у <= z true

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

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

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

Например, я пойду пешком от работы до метро, если на улице сухо (первое условие) и я никуда не тороплюсь (второе условие). Здесь мы сталкиваемся со сложным условием принятия решения, в котором присутствует два условия. Решение будет принято, если оба условия будут выполнены. Говоря языком программирования, данное логическое выражение будет иметь значение «истина» тогда, когда оба входящих в выражение условия будут иметь значение «истина».

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

Page 65: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

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

И, наконец, последняя операция называется отрицанием или «логическое НЕ». Она дает значение «истина», если операнд имеет значение «ложь», и значение «ложь», если операнд имеет значение «истина».

Обозначения: конъюнкция && или &, дизъюнкция || или |, отрицание !.

Разницу между && и & и между || и | мы обсудим ниже, пока будем пользоваться только обозначениями && и || для операций конъюнкции и дизъюнкции

Список значений операций при различных значениях операндов приведен в таблице 6.

Page 66: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Таблица 6

X У x && у x || у !x

false false false falsetrue

false true false true

true false false truefalse

true true true true

О приоритете операций. Операция отрицания является одноместной префиксной операцией и имеет такой же приоритет как другие одноместные операции. Следовательно, если мы хотим применить ее операции сравнения, то ее надо заключать в круглые скобки. Например, следующий фрагмент программы будет правильным:int х = 0; у = 1; boolean z = ! (х > у) ;

а аналогичный фрагмент, но без круглых скобок даст ошибку компиляции, так как в нем будет написано:int х = 0; у =■ 1; boolean z - !х > у;

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

Операции конъюнкции и дизъюнкции имеют приоритет ниже, чем операции сравнения, но выше, чем операция присваивания, поэтому следующий фрагмент программы будет правильным:int х = 0; у = 1; boolean z = х > 0 && у > 0;

Он присвоит переменной z значение false, так как одно из сравнений «х > 0» даст значение false.

Page 67: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Приоритет операции && выше, чем операции ||. Это соответствует нашему интуитивному представлению о том, что операция умножения имеет более высокий приоритет, чем операция сложения.

Возможные варианты вычисления знаменийарифметических операций приведены в проекте BooleanOperations, который содержит следующийпрограммный код:/*** <p>Title: логические операции</р>** <p>Description: Демонстрирует варианты вычисления* логических операций </р>

* <p>Copyright: Copyright (с) 2009</р>

* <р>Согг.рапу: </р>** @author not. attributabJe* @version 1.0*/

public class BooleanOperationsDemo {public static void main(String[] args) { boolean x, y; x = false; у = false;System.out.println("X = '* + V

+ "; у = " + у i x &S у = " + (X && У)+ "; x |i у = n + (x 11 У)+ ";\n !x = " + ! x) ;

x = false; у = true; System.out.println (" x = « t X

+ "; У = " + У + "; x S S у - " + (X У)+ "; x | ] у = "t • + (X i 1 У)I f

x - true; у = false; System.out.println (" X = и + X

+ "; у = " + у ■t "; x & & у = " + (X && У)+ "; x || у = " + (X 11 У)+ ; \n ! X = " + !x) ;

x = true; у =•■ true; System.out.println (”x = " + X

+ "; у = " + у+ "; x St s у = " t (X && У)+ "; x I 1 у = " + (X ! 1 У)

Page 68: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

) ;}

)После запуска эта программа дает следующий

результат:х = false; у = false; х && у - false; х I I у = false;!х = true

х = false; у = true; х & & у = false; х || у = true х = true; у = false; х & & у = false; х I| у = true;

!х = falseх = true; у = true; х &S у = true; х || у = true

Результаты работы этой программы подтверждают данные, приведенные в таблице 6.

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

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

Для того чтобы сделать наше рассмотрение более конкретным построим «свой мир», состоящий из точек, расположенных в квадрате со сторонами [0, 1] по оси X и [О, 1] по оси Y. Координаты точек будем представлять парой чисел двойной точности. Воспользуемся тем обстоятельством, что в состав языка входит класс Math, содержащих множество полезных математических функций в виде статических методов. В том числе метод random(), который вырабатывает при каждом вызове случайные числа, равномерно распределенные в диапазоне [0, 1), и метод sqrt (double х), возвращающий квадратный корень из числа х.

Предположим, что нам надо решить следующую задачу. Построить 4 точки, расположенных в единичном квадрате и найти ту из них, которая будет наиболее удалена от начала системы координат. Эту задачу решает проект MaxDistance, находящийся в директории Chapter03.

Построим для начала класс Point. Его описание будет выглядеть следующим образом./**

Page 69: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* <p>Title: Точка</р>к* <p>Descr.iption: Описывает точки,* расположенные на плоскости</р>

* <p>Copyright: Copyright (с) 2009</р>-к

* <p>Company: МИИТ </р>к* Sauthor Михайлюк А.В.* @vers ion 1,0 */

public class Point { private String name; private double x, у;

// конструкторpublic Point(String name, double x, double y) (

this.name - name; this.x = x; this.у = у;

/*** distance вычисляет расстояние точки* от начала координат */

public double distance!) {return Math.sqrtfx * x + у * у);

* метод возвращает строковое описание объекта:* имя точки и в скобках ее координаты по осям

* @return String V

public String toString i)treturn "Точка " t name -t "(" + x + ", " f у + ") ";

>

)

Обсудим этот класс. Он содержит три свойства, конструктор и два метода.

Обратимся к конструктору:public Point(String name, double x, double y) (

this.name = name;

Page 70: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

this.x = x; this.у = у;

}Он содержит три оператора присваивания. Он должен

присвоить свойствам name, х и у создаваемого объекта значения параметров передаваемых в конструктор в момент его вызова. Обратите внимание, что параметры тоже имеют имена name, х и у. Постое указание имен name, х и у в тексте программы дает доступ к параметрам метода. Для того, чтобы получить доступ к свойствам объекта, необходимо указать конструкцию “this.” This является одним из ключевых слов языка Java. Оно является указателем на тот объект, в теле которого это слово употреблено. Конструкцию «this.пате» надо понимать «свойство name текущего объекта».

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

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

Метод distance() вычисляет расстояние точки от начала системы координат, опираясь на теорему Пифагора. Метод состоит из одного оператора return, возвращающего

Page 71: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

вычисленное по формуле расстояние точки от начала системы координат.

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

Класс MaxDistance содержит метод main, который и выполняет всю полезную работу. Рассмотрим его подробнее./*■** <p>Title: MaxDistance</р>■*

<p>Descr Lption: строит 4 точки в заданном квадрате* и вычисляет точку наиболее удаленную от точки* начала координат</р>

* <p>Copyright: Copyright (с) 2009</р>** <p>Coinpany: </р>

* ^author not attributable* (aversion 1.0 */

public class MaxDistance (public static void main(String[] args) {

// списание точек нашего мира Point pi, p2, p3, p4;// построение точек нашего мираpi - new Point ("pi". Math.randomO, Math.. random ()) ; System.out.print In{"Построена " + pi

+ distance = " + pi.distance()); p2 = new Point("p2", Math.random(), Math.randomO); System.out.println("Построена " + p2

+ distance = " + p2.distance()); p3 = new Point !"p3", Math.randomO, Math.randomO);System.out.println("Построена " + p3

+ distance = " т p3.distance()); p4 = new Point("p4”, Math.random(), Math.randomO); System.out.println("Построена " + p4

Page 72: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

+ "; distance = " + p4.distance ());// нахождение точки наиболее удаленной// от начала координатPoint poantOfInterest = pi;double maxDistance = pi.distance ();if (p2.distance() > maxDistance)) pointOfInterest = p2; maxDistance = p2.distance ();

)if (p3.distance() > maxDistance)) pointOfInterest = p3; maxDistance = p3.distance();

}if (p4.distance() > maxDistance)) pointOfInterest = p4; maxDistance = p4.distance();

}System.out.println("наиболее удалена от начала"

+ " системы координат”);System.out.println(pointOfInterest

+ "; distance = "+ pointOfInterest.distance());

)Первая часть программы состоит из описания

переменных и ничего особенного собой не представляет:// описание точек нашего мира Point pi, р2, рЗ, р4;

Вторая часть состоит из четырех однотипных фрагментов текста:

// построение точек нашего мира pi = new Point ("pi", Math.randomO, Math.randomO); System.out.println("Построена " + pi

+ distance = " + pi.distance());

В первой строке идет вызов конструктора, создающего объект класса Point с параметрами - имя точки, координата х, координата у. Причем значения координат случайные числа в диапазоне от 0 до 1.

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

Page 73: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

классу Point, но этот объект автоматически приводится к строковому типу путем неявного вызова метода toStringf).

И, наконец, третья часть состоит из начального фрагмента:

Point pointOfInterest = pi;double maxDistance = pi.distance (>;и трех повторяющихся однотипных фрагментов:if {р2.distance() > maxDistance) { pointOfInterest - p2; maxDistance = p2.distance ();

)Начальный фрагмент вводит две служебные

переменные: переменную pointOflnterest типа Point,предназначенную для накопления в ней результата работы по мере рассмотрения точек, входящих в наш мир и переменную maxDistance типа double, хранящую расстояние точки pointOflnterest до начала системы координат. Алгоритм решения задачи рассматривает все созданные точки по очереди и на каждом этапе переменная pointOflnterest хранит ту точку, которая является наиболее удаленной от начала системы координат среди всех рассмотренных. Поэтому ясно, что на 1-м этапе, когда мы рассматриваем только точку pi, она у нас и является наиболее удаленной от начала системы координат из всех рассмотренных. При подключении очередной точки мы сравниваем удаленность новой точки от начала системы координат. Если ее удаленность меньше или равна той, что указана в переменных pointOflnterest и maxDistance, то это означает, что вновь рассмотренная точка не является кандидатом на решение поставленной задачи. Если же она находится дальше от начала координат, то она становится наиболее удаленной точкой из всех рассмотренных, поэтому мы заменяем значение переменных pointOflnterest и maxDistance на новые. Когда мы рассмотрим все точки, то переменная pointOflnterest будет хранить ответ нашей задачи, то есть указывать на точку, наиболее удаленную из всех от начала системы координат, а переменная

Page 74: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

maxDistance - удаленность этой точки от начала системы координат. Поэтому в конце программы стоят операторы.

System.out.println("наиболее удалена от начала"+ " системы координат");

System.out.println(pointOflnterest+ distance = "-t- pointOflnterest. distance () ) ;

которые выводят результат решения задачи на консоль.Приведенные выше рассуждения позволяют

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

1) написанная программа заканчивает свои вычисления за конечное число шагов

и2) полученный при этом результат является решением

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

быть уверенны в правильности нашего алгоритма.Вспоминая описание синтаксиса оператора if, мы

видим, что его общая форма имеет следующий вид:if (<условное выражением<оператор!>

ИЛИ

if (<условное выражением' <оператср1> else <оператор2>

То есть после конструкции if и после конструкции else должен стоять один оператор. А что делать, если после конструкции if или после конструкции else надо написать не один, а несколько операторов? Ответ на этот вопрос вы уже можете увидеть в первой же нашей программе, использующей оператор if. В языке имеется конструкция, называемая блоком операторов. Блок операторов - это последовательность операторов, заключенная в фигурные

Page 75: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

скобки. Блок операторов всегда рассматривается как один оператор. С блоком операторов связано еще понятие локализации переменных. Внутри блоков операторов можно описывать «свои» переменные. Если некоторая переменная описана внутри программного блока, то вне его она не доступна. Таким образом, если после конструкции if или else надо написать не один оператор, а несколько, то используйте фигурные скобки, чтобы объединить эти операторы в программный блок. На самом деле правильная методическая рекомендация состоит в том, чтобы приучить себя ВСЕГДА после условия в операторе if и после ключевого слова else писать фигурные скобки. Следование этому простому правилу избавит вас впоследствии от большого числа трудноуловимых ошибок в программах.

Перейдем к рассмотрению второй формы условного оператора. Конструкцияif Кусловное выражение:») <оператор!> else <оператор2>

исполняется так. Вычисляется значение логического выражения. Если оно рано true, то исполняется оператор 1, иначе исполняется оператор 2. после чего управление передается оператору, следующему за ним.

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

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

Page 76: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Рассмотрим проект Dislocation в директории Chapter03. Он содержит описание 2-х классов Point и Dislocation. Класс Point приведен ниже.! •*' •*:* <p>Title: Точка</р>к* <p>Description: Описывает точки,* расположенные на плоскости</р>к* <p>Copyright: Copyright, (с) 2009</р>

* <p>Company: МИИТ </р>к

* ^author Михайлюк А.В.* (Aversion 1.0*/

public class Point ( private String name; private double x, y;

// конструкторpublic Point(String name, double x, double y) {

this.name = name; this.x = x; this.у = у;

/*** метод возвращает строковое описание объекта:* имя точки и в скобках ее координаты по осям

Page 77: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* @return String */

public String toString (){return "Точка " + name + "(" + x + ", " + у +

j Hr ** getX */

public double getx() ( return v , ;

/*** getY * /

public double getYO { return y;

t

Он содержит методы getX() и getY(), которые обеспечивают доступ к основным свойствам точки — координатам по осям X и Y,

Описание класса Dislocation приведено ниже! * ** <p>Title:дислокация точек </р>

* <р>Description: определяет количество точек,* расположенных над диагональю единичного* квадрата, под диагональю и на диагонали</р>

* <p>Copyright: Copyright (с) 2009</р>** <р>Сотрапу: МИИТ </р>

* Sauthor Михайлюк А.В.* 0version l.C */

public class Dislocation {public static void main (String [) args) {

// описание точек нашего мира Point pi, p2, p3, p4;// построение точек нашего мираpi = new Point ("pi", Math.randomO, Math.randomO); System.out.println("Построена " + pi); p2 = new Point("p2", Math.randomO, Math.randomO); System.out.println("Построена " + p2);

Page 78: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

рЗ = new Point ("p3", Math.randomO, Math.randomO); System.out.println("Построена " + p3); double xy = Math.randomO; p4 = new Point("p4", xy, xy) ;System.out.print In("Построена ” + p 4);// описание счетчиков попадания точек в разные/,/ области единичного квадратаint underDiag = 0, overDiag = 0, onDiag = 0;// Поочередный анализ положения точек на // плоскостиif (pl.getXO < pl.getY{)){

overDiag++;)else if (pl.getXO > pl.getY()){ underDiag++;

)else{onDiag++;

)if (p2.getX() < p2.getY())( overDiag++;

jelse if (p2.getX() > p2.getY())( underDiag++;

}else{onDiag++;

}if (p3.getX() < p3.getY())(

overDiag++;lelse if (p3.getX() > p3.getY ())( underDiag+t;

}else{onDiag++;

)if (p4.getX() < p4.getY()){ overDiag++;

Jelse if (p4.getX() > p4.getY()){ underDiag++;

}else{onDiag++;

}// вывод результатовSystem.out.println("Над диагональю " f overDiag); System.out.println("Под диагональю " + underDiag); System, out. print.] n ("На диагонали " + onDiag);

Метод main состоит из нескольких разделов.Раздел 1. Содержит описание переменных,

составляющих наш мир объектов:// описание точек нашего мира

Page 79: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Point pi, p2, p3, p4;Раздел 2 состоит из построения нашего мира объектов,

аналогично тому, как мы это делали в проекте MaxDistance.// построение точек нашего мираpi - new Point ("pi", Math.randomO, Math.randomO);System.out.println("Построена " + pi);p2 = new Point("p2", Math.random(), Math.randomO);System.out.println("Построена " + p2);p3 ■- new Point("p3", Math.randomO, Math.random()) ;System.out.println("Построена " + p3);double xy = Math.randomO;p4 = new Point !"p4", xy, xy) ;System.out.println("Построена " + p4);

Единственная особенность присуща точке р4, которая строится так, чтобы обязательно попасть на диагональ. Иначе одна из ветвей нашей программы почти наверняка останется не пройденной.

Раздел 3 реализует алгоритм подсчета количества точек, находящихся выше диагонали, ниже диагонали или точно на диагонали. По числу точек он состоит из 4-х однотипных шагов. Рассмотрим подробно один из шагов:

if (pl.getXO < pl.getY()){ overDiag++;

lelse if (pl.getXO > pl.gett'OH underDiag++;

}else{onDiag++;

)В первой строке идет проверка условия pl.getXO <

pl.getY(). Методы pl.getXO и pl.getYO доставляют координаты точки р! по осям X и Y. Если условие выполнено, то эго однозначно указывает, что точка находится над диагональю. В этом случае выполняется увеличение счетчика числа точек, лежащих над диагональю и весь условный оператор считается выполненным. Управление передается следующему за ним оператору. Если условие не выполнено, то управление предается на часть else. Оператор, стоящий после слова else опять оказывается условным оператором и производится проверка второго условия pi .getXQ > pl.getYQ. Это условие будет выполнено,

Page 80: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

если точка pi находится под диагональю. Если это условие выполнено, выполняется блок {underDiag++;} и на этом выполнение всего оператора заканчивается. Если же это условие не выполнено, то уже безо всяких проверок мы попадаем на блок {onDiag++;}, то есть, увеличиваем счетчик точек, лежащих точно на диагонали.

Раздел 4 выполняет вывод результатов:// вывод результатовSystem.out.println("Над диагональю " + overdoing); System.out.println("Под диагональю " t underDiag); System.out.println("На диагонали " + onDiag);

Его действия очевидны и не требуют специального обсуждения.

Оператор switchЯзык Java имеет еще один оператор ветвления,

отличный от оператора if. Это оператор switch - переключатель. Принципиальным отличием этого оператора от оператора if является то, что он обеспечивает ветвление сразу на произвольное количество направлений. Оператор if принципиально осуществляет ветвление не более чем на 2 направления, поскольку принятие решения о выборе направления вычисления осуществляется на основерезультата вычисление значения логического выражения. А оно может дать только два результата true и false. Следовательно, и ветвление может быть только на направления. Если нам надо произвести ветвление на большее количество направлений, то мы вынуждены пользоваться цепочками условных операторов, как это было сделано в предыдущем примере.

Общая структура оператора switch приведена ниже:switch (<арифметические выражение>){

case оначекие 1>:сПоследовательность операторов 1>[break;]

case <значение 2>:Последовательность операторов 2>[break;]

Page 81: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

case «значение n>:«Последовательность операторов п>[break;]

[default;«последовательность операторов п+1>]

)Рассмотрим порядок исполнения оператора switch.Первым шагом происходит вычисление

арифметического выражения стоящего в скобках после ключевого слова switch. Арифметическое выражение может давать результат только типа byte, short, int и char.

Вторым шагом происходит сравнение вычисленного значения со значениями, записанными после ключевых слов case. Сравнения выполняются последовательно в порядке записи. Если вычисленное значение равно значению, записанному после ключевого слова case, то управление передается той последовательности операторов, которая расположена после того значения, которое совпало с вычисленным значением выражения.

Третьим шагом идет выполнение выбранной последовательности операторов. Операторы выполняются до тех пор, пока не встретится оператор "break;", который прерывает исполнение оператора switch и передает управление на операторы, следующие за оператором switch. Если действия по разным вариантам значения разные, то есть мы хотим, чтобы в случае <значения 1> срабатывала <последовательность операторов 1>, а в случае <значения 2> -^последовательность операторов 2>, то запись оператора break; после -^последовательности операторов 1> обязательна. Если мы не запишем оператор break; после <последовательности операторов 1>, то в случае равентсва <арифметического выражения> <значению 1> сначала выполнится «^последовательность операторов 1>, а затем подряд «^последовательность операторов 2>, и так далее либо: до ближайшего оператора break; либо до конца оператора switch. Это может дать результат, на который программист совсем не рассчитывал. Самое опасное при

Page 82: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

этом, что ни транслятор, ни исполнительная система не предупредят вас о том, что вы пропустили оператор break; потому, что его присутствие не является обязательным. Вы можете совершенно сознательно писать несколько фраз case с разными значениями, предписывая им одинаковую обработку. Одним словом, практическая рекомендация такова: если пишете предложение case внутри оператора switch, не забудьте в конце обработки перед следующим case обязвтельно написать break;

В случае если вычисленное значение не совпало ни с одним из значений, указанных в предложении case, то управление получает последовательность операторов после предложения default:. Если предложение default: в операторе не написано, то оператор switch никаких действий не выполняет, и предает управление на оператор, следующий за ним.

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

Рассмотрим проект Dislocation2 в директории Chapter03. Он содержит описание тех же классов, что проект Dislocation.

Класс Point выглядит следующим образом:j * ** <p>Title: Точка</р>Чг

* <p>Description: Описывает точки,* расположенные на плоскости</р>** <p>Copyright: Copyright (с) 2009</р>Чг* <p>Company: МИИТ </р>

Page 83: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* ©author Михайлюк А.В.* ©version 1.0 * /

public class Point {// описание глобальных констант public static final int OVER_DIAG = -1

, ON_DIAG = 0, UNDER_DIAG = 1;// описание свойств объектов private String name; private double x, y;

// конструкторpublic Point(String name, double x, double y) {

this, name =• name; this.x = x; this.у = у;

! *• ★* метод возвращает строковое описание объекта:* имя точки и в скобках ее координаты по осям*

* ©return String* /

public String toString (){return "Точка " + name + "(’’ + x f ", " + у +

}/*** dislocation определяет положение точки Ht относительно диагонали единичного квадрата V

public int dislocation() { if (x < у)(

return OVER_DIAG;)if (x > y)(

return UNDER_DIAG;}return ON DIAG;

При рассмотрении класса Point, прежде обращаем внимание на следующие строки:

// описание глобальных констант public static final int OVER_DIAG = -1

, ON DIAG - 0, UNDER_DIAG = 1;

всего,

Page 84: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Этот фрагмент программы является описанием констант. Имена констант OVER DIAG, ON DIAG = 0 и UNDER DIAG. В соответствии с традицией языка Java константы записываются заглавными буквами, отдельные слова, входящие в идентификаторы отделяются друг от друга символом подчеркивания. Но самое главное - это спецификаторы, которые сопровождают их описание. Разберем их по очереди. Спецификатор public нам уже давно знаком и тут он играет свою обычную роль: говорит, что описываемые идентификаторы будут доступны для использования вне того класса, в котором они описаны. Спецификатор static мы уже обсуждали выше применительно к методам. Применительно к свойствам он означает, что свойства, специфицированные как static, связаны не с экземплярами объектов, а с классом в целом, то есть они существуют в одном экземпляре в составе класса. Спецификатор final мы встречаем здесь в первый раз. Применительно к свойствам он означает, что значения этих свойств не могут быть изменены. То есть свойства, специфицированные как final, не могут использоваться как приемники информации в операторах присваивания. Иными словами это константы, которые получают свои значения в момент описания и дальше доступны для использования, но измениться уже не могут. Спецификатор int задает тип данных. В его использовании тут нет ничего необычного. Методику использования констант мы продемонстрируем в классе Dislocation. Главное в использовании констант - обеспечить однотипное использование числовых значений, обозначающих разные состояния объекта во всех местах программы, где они нужны. Почти всегда не валено, какими именно числами обозначены те или иные состояния объектов, а важно их однотипное использование во всех частях проекта.

Дальнейшее изучение класса Point показывает, что он мало отличается от аналогичного класса описанного в проекте Dislocation. При чем отмечаем два отличия:

Page 85: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

1) отсутствуют методы getX() и getY(), они в данном проекте не нужны;

2) появился новый методpublic int; dislocation;) {

if (x < у) {return OVER DIAG;

}if (x > y) {

return UNDER_DIAG;}return ON_DTAG;

}Этот метод вычисляет положение точки относительно

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

Теперь, кода мы познакомились классом Point перейдем к рассмотрению класса Dislocation./ * *** <p>Title:дислокация точек </р>** <p>Description: определяет количество точек,* расположенных над диагональю единичного* квадрата, под диагональю и на диагонали</р>

* <p>Copyright: Copyright (с) 2009</р>+* <р>Сошрапу: МИИТ </р>

* @author Михайлюк А.В.* (Aversion 1.0 */

public class Dislocation-{public static void main(String[1 args) {

/7 описание точек нашего мира Point, pi, p2, p3, p4;

Page 86: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// построение точек нашего мираpi = new Point("pi", Math.randomO, Math.randomi))System.out.println("Построена " + pi);p2 = new Point ("p2", Math.randomO, Math.randomO!System.out.println("Построена " + p2);p3 = new Point ("p3", Math.randomO, Math.randomO)System.out.println("Построена " + p3) ;double xy = Math.randomO;p4 = new Point ("p4", xy, xy) ;System.out.println("Построена " + p4);// описание счетчиков попадания точек в разные// области единичного квадратаint underDiag = 0, overDiag = 0, onDiag = 0;// Поочередный анализ положения точек на плоскости switch(pi.dislocation О ){

case Point.OVER_DIAG: overDiag++; break;

case Point.UNDER_DIAG; underDiag++; break;

case Point.ON_DIAG: onDiag++;

}switch(p2.dislocation()){

case Point.0VER_DIAG: overDiag++; break;

case Point.UNDER_DJAG: underDiag++; break;

case Point.ON_DIAG: onDiag++;

}switch(p3.dislocation!)){

case Point.OVER_DIAG: overDiag++; break;

case Point.UNDER_DIAG: underDiag++; break;

case Point.ON_DIAG: onDiag++;

iswitch(p4.dislocation() ) {

case Point.OVER_DIAG: overDiag++; break;

case Point.UNDER_DIAG: underDiag++; break;

Page 87: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

case Point.0N_DIAG: onDiag++;

)// вывод результатовSystem, out.. println ("Над диагональю " + overDiag); System.out.println("Под диагональю " + underDiag); System.out.print In("На диагонали ” + onDiag);

Анализ текста метода main показывает, что он почти точно повторяет аналогичный текст из проекта Dislocation за исключением части, связанной с вычислением счетчиков попадания точек в разные части единичного квадрата. Рассмотрим ее более подробно.

// Поочередный анализ положения точек на плоскости switch(pi.dislocation()){

case Point.0VER_D1AG; overDiag++; break;

case Point.UNDER_DIAG: underDiag+t; break;

case Point.ON_DIAG: onDiag++;

}Для анализа вариантов попадания точки в разные части

единичного квадрата применен оператор switch. В качестве арифметического выражения используется вызов метода pl.disiocation(), который, как мы знаем из рассмотрения класса Point, возвращает одну из констант, описанных в этом классе и ничего другого. Поэтому в предложениях case указаны именно эти константы, как возможные варианты значения арифметического выражения. Обратите внимание, что константы указываются путем указания имени класса, в котором они определены и через точку записывается имя константы. Такова форма использования констант вне класса, в котором они описаны.

Запуск программы на исполнение даст результат:Построена Точка р1(0.2773636868466096, 0.3306971125552226)

Построена Точка р2 ( 0.2619732630284479, 0.8030255723915131)Построена Точка рЗ(0.9981205484125315,

Page 88: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

0.04211781302577877)Построена Точка р4(0.7766557829340013, 0.7766557829340013)Над диагональю 2 Под диагональю 1 На диагонали 1

Анализ результата показывает, что задача решена правильно.

ВыводыЗаканчивая этот большой раздел, мы должны сказать,

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

Без операторов ветвления в той или иной форме программирование невозможно.

В языке присутствуют два оператора ветвления: условный оператор if и переключатель switch. Области применения этих операторов несколько отличаются и программисту надо уметь выбрать подходящий к его задаче оператор ветвления.

Кроме того, мы познакомились с логическими переменными, логическими операциями и логическими выражениями.

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

Page 89: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Повторное выполнение однотипных действий - единственный способ обработки больших объемов информации. Операторы цикла - средства описания повторяющихся действий объекта.

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

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

Page 90: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Оператор цикла forВ языке Java есть несколько разных операторов цикла.

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

Самый широко распространенный и любимый программистами оператор цикла это оператор for. Он имеет следующую структуру:for (<начальные действия>; <условие выполнения;»;

оавершающие действия>)<оператор>Опишем порядок исполнения оператора for.

Исполнение оператора начинается с исполнения действий, описанных в части <начальные действия>, Эта часть оператора for отрабатывает всегда и при том в точности один раз. Далее вычисляется значение логического выражения <условие выполнения^ Если условие имеет значение true, то исполнение цикла продолжается, если false, то оператор цикла считается выполненным, и управление предается оператору, следующему за оператором for. Оператор, стоящий за круглой скобкой, называется телом цикла. После выполнения тела цикла исполняются действия, описанные в части Завершающие действиям За тем опять проверяется истинность логического выражения <условие выполнения^ Если оно истинно, то повторяется выполнение тела цикла и Завершающие действиям Как только при очередной проверке <условие выполнения> получает значение false, исполнение оператора цикла заканчивается.

Page 91: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Несмотря на несколько пространное описание смысла оператора for, понять его легко. Все входящие в него части логичны и прозрачны. Рассмотрим простейший пример оператора for, приведенный в проекте SimpleFor в директории Chapter03:/**

* <p>Title: Простой цикл for</p>** <p>Description: демонстрирует простейшее применение* оператора цикла для печати последовательности* целых чис.ел</р>

* <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р>

* Sautho.r not attributable* (Aversion 1.0*/

public class SimpleFor {public static void main(String[] args) {

for (int i = 0; i < 10; i++) lSystem, out .println ("Исполнение цикла c .i = " + i);

))

После запуска программа напечатает:Исполнение цикла с i = 0

Исполнение цикла с i = 1 Исполнение цикла с i = 2 Исполнение цикла с 1 = 3 Исполнение цикла с i = 4 Исполнение цикла с 1 = 5 Исполнение цикла с i = б Исполнение цикла с i = 7 Исполнение цикла с i = .8 Исполнение цикла с i = 9

Page 92: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Разберем подробно оператор цикла. Конструкция for (int i = 0; i < 10; i++) называется заголовком цикла. Конструкция {System.оибрппбп("Исполнение цикла с i = " + i);} называется телом цикла. Выражение int i = 0 описывает начальные действия. Оператор цикла создает неявный программный блок, включающий заголовок и тело цикла. В рамках этого блока можно описать переменные, в данном случае описана переменная int i. Эта переменная доступна для использования и изменения в заголовке цикла и в теле цикла. Переменная, описанная в заголовке цикла, называется параметром цикла. Возьмите за правило, никогда, ни при каких обстоятельствах не изменять параметр цикла в теле цикла. Это избавит вас от большого числа проблем, связанных с доказательством корректности цикла и, соответственно, с правильностью ваших программ. Условие i < 10 обеспечивает нам исполнение тела цикла для i, пробегающего значения от 0 до 9 включительно, то есть ровно 10 раз. Выражение i++ выполняет продвижение параметра цикла на 1 после каждого выполнения тела цикла. Оператор {System.out.println("HenoaHerme цикла с i = " + i);} является программным блоком, состоящим из одного оператора печати, использующим при формировании текста для вывода на печать параметр цикла.

При написании операторов цикла мы должны заботиться о двух вещах:

1) мы должны быть уверены, что написанный нами цикл завершается за конечное число шагов;

2) мы должны быть уверены, что каждый шаг приближает нас к решению поставленной перед нами задачи. Формулируя это же требование по другому, можно сказать, мы должны гарантировать, что обработанная часть данных отвечает некоторому условию, которое остается неизменным на протяжении всего времени выполнения цикла и остается истинным после выхода из цикла. Условие, остающееся неизменным на протяжении всего времени исполнения цикла называется инвариантом цикла.

Page 93: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

1) Поскольку параметр цикла в теле не меняется, из записи заголовка цикла видно, что параметр пробегает значение, начиная с 0, увеличиваясь в каждом цикле на 1. При достижении параметром цикла значения 10 условие выполнения приобретает значение false , и цикл завершится.

2) Цель программы - печать значений параметра цикла. Поэтому инвариант формулируем так: для любого 0 <= k <= i на консоль должно быть выведено сообщение об использовании параметра цикла со значением к. По окончании цикла инвариант будет выглядеть так: для любого 0 <= к <= 9 на консоль выведено сообщение об использовании параметра цикла со значением к. Вид инварианта цикла после выхода из цикла напрямую говорит нам, что поставленная задача решена.

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

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

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

Page 94: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Для демонстрации более содержательного доказательства корректности работы программы решим еще раз задачу нахождения точки наиболее удаленной от начала системы координат. Только будем решать ее не для 4-х точек, как это было в проекте MaxDistance, а для 1000 тысяче точек, что практически лишает нас возможности визуально проверить правильность работы программы. Построим проект MaxDistance2 в директории Chapter03.

Класс Point мы берем точно такой же, как и в проекте MaxDistance. Поэтому в целях экономии места мы его не

Page 95: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

будем приводить здесь повторно, а вот класс MaxDistance поменялся довольно сильно. Рассмотрим его подробно/**

* <p>Title: MaxDistance</p.>■к* <p>Description: строит 4 точки заданном квадрате* и вычисляет точку наиболее удаленную от точки* начала координат</р>к* <p>Copyright: Copyright (с) 2009</р>** <р>Сошрапу: </р>■к* @author not attributable* Inversion 1.0* /

public class MaxDistance (public static void main(String[] args) {

// описание течек нашего мира Point р = new Point("точка 0”

, Mat.h. random (), Math.random() ) ;

// нахождение точки наиболее удаленной // от начала координат Point pointOflnterest = р; double maxDistance = р .distance (); for (int i = 1; i < 1000; i++){ p - new Point. ("точка " t i

, Math.random(), Math.random());

if (p.distance() > maxDistance) { pointOflnterest ■= p; maxDistance = p .distance();

)System.out.println("наиболее удалена от начала"

+ " системы координат");System.out,println(pointOflnterest

+ "; distance = " i pointOflnterest.distance!));

i

Результат работы программы после одного из запусков:наиболее удалена от начала системы координат

Точка 978(0.9871608818271682, 0.9998609652589169); distance = 1.4050653210645707

Page 96: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

Доказательство будем вести методом полной математической индукции.

Шаг 1. Для точки 0 теорема верна.Доказательство.Когда построена точка 0, срабатывают следующие

операторы:Point р = new Point("точка О"

, Math.random(), Math.random());

Point pointOflnterest = p;double maxDistance = p .distance ();Поскольку создана только одна точка, то она же и

будет наиболее удаленной от начала системы координат.Доказательство закончено.Шаг 2. Пусть для некоторого i (i > 0 и i < 1000)

переменная pointOflnterest содержит указатель на точку наиболее удаленную от начала системы координат, и переменная maxDistance - расстояние этой точки от начала системы координат среди всех точек от 0 до i-1-й. Тогда после построения i-ой точки переменная pointOflnterest

Page 97: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

будет хранить указатель на точку наиболее удаленную от начала системы координат среди всех точек от 0 до i-ой.

Доказательство.Строим новую точку операторомр = new Point("точка " + i

, Math.randomO , Math.random());

Анализируем удаленность новой точки от начала системы координат. Если новая точка находится дальше от начала системы координат, чем точка, на которую указывает переменная PointOflnterest, то это означает, что вновь построенная точка находится дальше от начала системы координат, чем все предыдущие и необходимо заменить указатель pointOflnterest, установив его на вновь построенную точку. В противном случае она находится не дальше от начала системы координат, чем все предыдущие точки и значение переменной pointOflnterest меняться не должно. Что и отражают следующие операторы.

р = new Point("точка " + i, Math.random(), Math.randomO);

if (p.distance() > maxDistance) ( pointOflnterest = p; maxDistance = p.distance();

}Доказательство закончено.Из двух доказанных теорем следует, что после выхода

из цикла для i = 1000 переменная pointOflnterest указывает на точку наиболее удаленную от начала системы координат среди всех 1000 построенных точек.

Оператор цикла whileРанее уже упоминалось, что в языке предусмотрено

несколько вариантов оператора цикла. Рассмотрим следующий вариант оператора цикла. Оператор while. Он имеет следующий синтаксис:while (<условие выполнения;») <оператор>

Page 98: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Исполняется он следующим образом:Вычисляется булево выражение <условие

выполнения>. Если оно имеет значение false, то оператор считается выполненным и управление передается оператору, стоящему за оператором while. Если <условие выполнения> имеет значение true, то исполняется <оператор>, записанный после условия, и далее опять вычисляется значение выражения <условие выполнения^

Операторы for и while эквивалентны. Все, что можно записать с помощью оператора for можно написать с помощь оператора while и наоборот. Но на практике их использование таит некоторую разницу. Как правило, если количество повторений тела цикла известно заранее, то используют оператор for. Если количество повторений не известно заранее, то предпочитают использовать оператор while

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

Page 99: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

Класс List сложнее. Он должен уметь добавлять элементы в очередь и искать элементы в списке. В данном примере мы не будем реализовывать полный набор методов работы со списками.

Рассмотрим описание класса Item/**

* <p>Title: Элемент списка </р>к* <p>Description: Описывает элемент списка* целых чисел</р>

* <p>Copyrigbt: Copyright (с) 2009</р>к* <p>Company: МИИТ</р>к* Sauthor Михайлюк* @vers ion 1.0*/

public class Item {// глобальный счетчик элементов, используется // для построения уникальных идентификаторов // элементов списка private static int counter = 0;

// свойства отдельного элемента очереди private int id; // уникальный идентификатор private int value; // значение элемента очереди private Item next; // ссылка на следующий элемент или // null// конструктор public Item(int value) {

id = t+counter;

Page 100: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

this.value = value;

public int getValue{){ return value;

}public Item getNext(){

return next;)public void setNext (Item n){

next = n;

public String toString(){ return "" + id + ") "

+ value + " "+ (next == null ? "последний" : "следующий

+ next.id);

iНесколько моментов в этом тексте требуют нашего

отдельного внимания.Рассмотрим следующий фрагмент текста:

// глобальный счетчик элементов, используется // для построения уникальных идентификаторов // элементов списка private static int counter = 0;

В этом фрагменте описывается свойство counter, которое имеет спецификатор static. В сочетании со спецификатором private создается целое приватное свойство, принадлежащее классу, а не объекту. Проследите за использованием этого свойства. Оно используется только в конструкторе, чтобы построить уникальный идентификатор объекта. Сколько бы ни создавали объектов, каждый из них будет получать свой уникальный номер. Для этого в конструкторе имеется строка

id = ++counter;В ней происходит приращение счетчика и присвоение

измененного значение свойству id, которое впоследствии

Page 101: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

используется в методе toString для идентификации элемента списка.

Следующий фрагмент демонстрирует применение новой для нас операции, называемой условным выражением оно имеет следующий синтаксис:слог'лческое выражение? ? <выражекие 1> : <выражение 2>

Вычисляется условное выражение по следующему правилу. Вычисляется Логическое выражением Если оно имеет значение true, то значением всего выражения считается значение <выражения 1>. Если Логическое выражение> имеет значение false, то значением условного выражения становится значение <выражения 2>. Пример применения логического выражения мы встречаем в методе toString:

+ (next. == null ? "последний" : "следующий "+ next.id);

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

Рассмотрим текст класса List. В целом эго класс отвечает за создание списка, подержание его целостности и манипулирование со списком./*** <p>Titie: Список</р>■к

* <p>Description: класс описывает управление* списком целых чисел</р>•к* <p>Copyright; Copyright (с) 2009</р>

* <р>Сол;рапу: МИИТ</р>

* Sauthor Михайлюк А.В.* Diversion 1.0V

public class List (private Item first; //ссылка на 1-й элемент списка private Item last; // ссылка на последний элемент списка

Page 102: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// конструктор public List () {

first = null; last = null;

}// добавление элемента списка public void add(int value){

Item newElement = new Item(value); if (first == null){

first = newElement;} else {

last.setNext(newElement);}last = newElement;

// поиск заданного числа в списке public Item find (int value){

if (first == null){ return null;

} else (Item curr = first; while (curr != null){

if (curr.getValue () == value)! return curr;

}curr = curr.getNext();

}return null;

public String toString!)( if (first == null){

return "Список пуст";) e 1 s e {String result = "Содержимое списка:"; Item curr = first; while (curr '= null))

result += "\n " + curr;curr = curr.getNext();

)return result;

)}

Рассмотрим отдельные фрагменты представляющие интерес для нашего рассмотрения.

текста

Page 103: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Конструктор всегда создает пустой список:// конструктор public List!) {

first - null; last = null;

Метод add() всегда создает новый элемент списка и добавляет его в качестве последнего элемента списка:

// добавление элемента списка public void add(int value){

Item newElement = new Item(value); if (first == null){

first = newElement;) else {last.setNext(newElement);

Ilast = newElement;

)Обратите внимание, что добавление первого элемента

списка отличается от добавления любых других элементов,Метод find осуществляет поиск элемента списка с

заданным свойством value:// поиск заданного числа в списке public Item find (int value))

if (first == null)( return null;

) else {Item curr = first; while (curr != null)(

if (curr.getValue() == value)! return curr;

}curr = curr.getNext();

}return null;

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

Page 104: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рассматриваемый метод наглядно демонстрирует применение оператора while. Условием продолжения выполнения тела цикла является непустая ссылка на текущий элемент. Если она стала пустой, то это означает, что все элементы списка просмотрены, и дальнейший поиск в списке не имеет смысла. Поэтому условием продолжения поиска является выражение curr != null. Очень важным оператором является оператор curr = curr.getNextQ; который обеспечивает переход к рассмотрению следующего элемента списка. Если мы его забудем написать, то программа зациклится на первом же элементе списка. Этот же метод можно переписать, используя оператор цикла for следующим образом:

// поиск заданного числа в списке public Item find (int. value) (

if (first == null){return null;

} else {for (Item curr = first; curr != null

; curr = curr.getNext()){ if (curr.getValue() == value){

return curr;}

}return null;

Какой из двух способов записи цикла предпочесть - дело вкуса программиста

Оператор цикла do ... whileПоследний из рассматриваемых операторов цикла

do. ..while имеет следующую структуру:do <оператор>while (<ус.ловие повторениям ;

Исполняется он следующим образом. Сначала исполняется <оператор>, образующий тело цикла, а потом вычисляется логическое выражение <условие повторения^ Если оно имеет значение true, то <оператор> исполняется повторно и так до тех пор, пока <условие повторения> не

Page 105: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

получит значения false, после чего управление получит оператор, следующий за оператором цикла.

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

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

Исключительные ситуации. Их возникновение и обработка

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

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

Page 106: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Рассмотрим случай, когда программисту известно, что некоторая последовательность операторов может в процессе исполнения вызывать возникновение исключительной ситуации определенного класса, и он может воспользоваться следующей конструкцией:try{сохраняемые операторы>}catch(<имя класса исключительной ситуэциихимя объекта>){ Собработка исключительной ситуацию I

Рассмотрим, как исполняется эта конструкция. Начинают исполняться сохраняемые операторы>, записанные после ключевого слова try, один за другим, как обычно. Если в ходе исполнения возникает исключительная ситуация, то нормальный ход исполнения программы прерывается, создается объект, описывающий возникшую ситуацию. Если класс созданного объекта совпадает с указанным после ключевого слова catch, то этот объект отожествляется с объектом, указанным после <имени класса исключительной ситуации>. После этого управление передается на операторы, выполняющие <обработку исключительной ситуации>. Если <обработка

Page 107: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Посмотрите текст приведенного ниже проекта ZerroDevision, который поясняет принцип перехвата исключительных ситуаций и их обработки. Имя исключительной ситуации, которая может возникнуть при исполнении программы AruthmeticException./*** <p>Tit.le: деление на ноль</р>

* <p>Description: демонстрирует обработку* перехвата исключительной ситуации,* возникающей при попытке деления на ноль</р>■*:* <p>Copyright: Copyright (с) 2009</р>

* <р>Сошрапу: МИИТ</р>** ©author Михайлкж А.В.* ©version 1.0*/

public class ZerroDevision {public static void main(String[] args) {

for (int i = 0; i < 20; i++){// получение случайного числа в диапазоне // от 0 до 9int random = intRandom(10);// печать полученного числа System.out.print

("получение случайного числа 100 / "+ random + " = ");

// начало охраняемых операторов try {

// попытка целения на случайное число // возможно, попадется 0, тогда деление // не удастсяint result = 100 / random;// печать результата деления получит // управление только, если random != 0

Page 108: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System, out .println (result) ;// перехват исключительной ситуации }catch(ArithmeticException ex)(

1 1 печатает сообщение об ошибке,// если делитель == ОSystem.out.println("деление на 0 невозможно");

}

J + ** intRandom возвращает случайное целое* в диапазоне от 0 до value - 1*

* @param value int - диапазон случайных чисел */

private static int intRandom(int value) { return (int) (value * Math.randomO);

}

Чтобы оценить работу программы, посмотрите на одну из реализаций запуска программы:получение случайного числа 100 / 1 = 100

получение случайного числа 100 / 3 = 33получение случайного числа 100 / 1 = 100получение случайного числа 100 / 7 = 14получение случайного числа 100 / 1 = 100получение случайного числа 100 / 7 = 14получение случайного числа 100 / 0 = деление на 0невозможнополучение случайного числа 100 / 9 = 11получение случайного числа 100 / 4 = 25получение случайного числа 100 / 2 = 50получение случайного числа 100 / 5 =■■ 20получение случайного числа 100 / 0 = деление на 0невозможнополучение случайного числа 100 / 7 = 14получение случайного числа 100 / 9 = 11получение случайного числа 100 / 8 = 12получение случайного числа 100 / 4 = 25получение случайного числа 100 / 9 - 11получение случайного числа 100 / 0 = деление на 0невозможнополучение случайного числа 100 / 0 = деление на 0невозможнополучение случайного числа 100 / 7 = 14

Page 109: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Пример программы, использующей цикл do...whileДля того чтобы стал понятен пример, сделаем

следующие замечания.В языке Java вывод данных предполагает

безошибочную работу устройств, а ввод данных может вызывать исключительную ситуацию IOException, означающую ошибку ввода\вывода аппаратуры.

В языке Java определены специальные классы, предназначенные для оборачивания примитивных переменных и свойств в оболочку классов, если возникает необходимость с простой переменной работать, как с объектом. Эти классы имеют свои методы, в том числе для переменных типа int существует класс Integer. Среди прочих он имеет статический метод int parselnt(String line), который переводит строку, содержащую целое число, в целое число. Если строка не содержит строкового представления целого числа, то этот метод вызывает исключительную ситуацию NumberFormatException. Посмотрите на текст программы:import java.io.IOException;/**

* <p>Title: Ввод целого числа</р>** <p>Description: программа предлагает* пользователю ввести целое число, пытается* перевести введенную пользователем строку в* целое число. Если это не удается, то просит* пользователя повторить ввод числа и так до■* тех пор, пока пользователь не введет целое* числс</р>

* <p>Copyright: Copyright (с) 2009</р>к

* <p>Company: МИИТ</р>к* 0author Михайлюк А.В.* Aversion 1.0* /

public class ReadData (public static void main(String[] args) {

int value = 0;try {// попытка ввода данных из входного потока

boolean cont = true; // устанавливаем признак // продолжения работы, он будет сброшен // в случае, если пользователь правильно

Page 110: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

//введет целее числоSystem.out.println{"Ведите целое число"); do{ // цикл ввода строк данных от пользователя

String line = // строка, куда мы// посимвольно вводим данные пользователя char с; // очередной вводимый //пользователем символdo{ // цикл посимвольного ввода данных

с = (char) System.in.read(); // ввод // очередного символа

if (с !-- '\п')( // добавляем к строке // любой введенный символ кроме // символа перевода строки line += с;

}}while (с != '\п1);// по символу конца // строки выходим из цикла ввода символов try{// попытка перевода строки в целое число value = Integer.parselnt(line); // если //строка не содержит целое число, то // возникает исключительная ситуация // класса Number Format-Exception cont = false; // если перевод строки в // число выполнился нормально, то // исполняется оператор, который // сбрасывает признак, иначе до него // управление не доходит и цикл ввода // данных повторяется

}catch (NumberFormatException ex){ // перехват //исключительной ситуации и ее обработка System.out.println("Повторите ввод числа");// обработка заключается в выдаче // пользователю сообщения о его ошибке

}}while (cont); // выход из цикла только если// мы благополучно прошли преобразование// строки в число// выдача результата ввода числаSystem.out.println("Ведено число " + value);

Jcatch (IOException ex) { // перехват исключительной //ситуации ошибки устройства ввода данных ex.printStackTrace(); // вывод на печать // подробной информации о цепочке вызовов // методов в момент возникновения // исключительной ситуации

})

)

Page 111: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

- Ill -

Посмотрите на результат работы программы, если пользователь сначала ввел неправильное значение «йцук», а со второй попытки - правильное «1234»:Ведите целое число

йцукПовторите ввод числа

1234Ведено число 1234

Вызов методов - способ заставить объект действовать. Передача параметров - один из способов обмена информацией между объектами.Способы вызова методов

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

Итак, почему же необходимы методы?Во-первых, надо помнить, что современный взгляд на

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

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

Page 112: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

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

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

Если метод не возвращает значения, то выход из него может осуществляться с помощью оператора return; без операнда или после выполнения последнего оператора в методе. Если же метод должен возвращать значение, то выход из него может осуществляться только с помощью оператора return <выражение>; где <выражение> вырабатывает значение того типа, которое указано в заголовке метода.

Page 113: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Спецификация метода выглядит следующим образом<спецификаторы> «имя метода> ([<список параметров>))

«спецификаторы исключительных. ситуаций> { «последовательность операторов>1

<спецификаторы> нам известны:Спецификаторы видимости> - всего их 4 - private,

protected, public и пустой спецификатор видимости. Мы их уже обсуждали выше.

Спецификатор статичности> - static или пусто. Этот спецификатор мы обсуждали выше в данном разделе

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

Спецификаторы исключительных ситуаций > выглядят следующим образом:throws «имя класса исключительной ситуации 1>[, < имя

класса исключительной ситуации 2>]...Эта спецификация означает, что описываемый метод

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

Page 114: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Контсрукция Последовательность операторов> представляет собой тело метода. Она описывает поведение объекта.

Если мы хотим вызвать из одного метода некоторого объекта метод того же объекта, то вызов выглядит следующим образом:<имя метода> ([<список параметров>]);

Например, если в некотором классе мы имеем следующее описание метода:

private void alpha!){System.out.println ("Вызван метод alpha");

)то вызов этого метода в некотором другом методе того

же класса будет выглядеть следующим образом:private void beta(){

alpha();

}Если метод возвращает значение, то его вызов может

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

коробки. Каждая коробка характеризуется длиной (length), шириной (width) и высотой (height). В этом случае мы можем написать метод, вычисляющий объем коробки путем умножения ширину на высоту и на длину. Для этого напишем метод public double volume(), который возвращает значение типа double. Это означает, что вызов этого метода может осуществляться путем использования имени метода вместе с круглыми скобками в выражении, что видно из реализации метода public String toString().public class Box {

private double length, width, height;

Page 115: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public Box (double 1, double w, double h){ length = 1; width = w; height = h;

public double volume(){return length * width * height;

public String toString(){ return "Коробка"

+ "\пдлина = " + length + "; ширина = " + width + высота = " + height+ "Хпобъем = " + volume!);

Если метод вызывается из другого класса, перед именем метода необходимо указать имя объекта, к которому относится этот метод. Имя метода отделяется от имени объекта точкой. Этот способ вызова того же метода volume() можно проследить на примере класса BoxDemo:public class BoxDemo {

public static void main(String[] args) {Box box = new Box(3, 4, 5);System.out.println(box); double -vol = box.volume();System.out.println("Коробка имеет объем " + vol);

В этом примере вызов метода volume() осуществляется в операторе описания переменной vol в инициализирующем выражении. Для того чтобы убедиться, что вызовы метода работают, посмотрите на результат запуска программы. Она находится в директории Chapter03 проект BoxDemo:Коробкадлина = 3.0; ширина = 4.0; высота =5.0 объем = 60.0Коробка имеет объем 60.0

Page 116: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Способы передачи параметров методамОстановимся на рассмотрении параметров методов.

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

При описании метода параметры записываются после имени метода в круглых скобках. Параметры указанные после имени метода в описании метода называются формальными параметрами.

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

Как описываются формальные параметры? Формальные параметры записываются в виде списка пар: тип параметра имя параметра. Элементы списка формальных параметров разделяются запятой. Тип параметра - либо один из примитивных типов, предопределенных в языке, либо имя класса. Имя параметра - произвольный идентификатор. Пример задания формальных параметров:private int summa (int x, i.nt y) {

return x + y;

В приведенном примере метода summa описаны два параметра. Первый параметр имеет тип int и имя х. Второй параметр имеет тип int и имя у.

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

Page 117: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Как связываются формальные и фактические параметры? При вызове метода фактические параметры последовательно вычисляются слева направо, и вычисленные значения присваиваются формальным параметрам. Так, если мы вызываем описанный выше метод summa, то вызов может выглядеть следующим образом:int z = summa (2, 3);

в процессе выполнения вызова будут исполнены следующие действия:(int х = 2; int у = 3; int summa = х + у; int z = summa;

}Таким образом, вызов метода заключается в

следующих шагах:• Строится программный блок, включающий вызов

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

совпадающие по типам и названиям с формальными параметрами метода.

• Им присваиваются начальные значения, равные заданным фактическим параметрам.

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

• Оператор return превращается в оператор присваивания значения выражения переменной результата.

• Исполняется тело метода. При этом переменная результата получает конкретное значение.

• В выражении, использующем вызов метода, этот вызов заменяется на временную переменную с результатом вызова метода.

Page 118: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

1) если формальный параметр имеет тип простой переменной, то когда происходит присваивание значения параметру значение переменной копируется. Это означает, что если внутри метода параметр изменит свое значение, то это не повлечет изменения значения той переменной, которая была использована в качестве фактического параметра;

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

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

Класс, описывающий коробки нам знаком, он просто дополнен методами доступа к свойствам:public class Box {

private double length, width, height;public Box (double 1, double w, double h){

length = 1; width = w; height = h;

public double getLength{){ return length;

}public double getWidth(){

Page 119: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

return width;

public double getHeight(){ return height;

public void setLength(double value){ length = value;

public void setwidth(double value){ width = value;

}public void stHeight(double value){

height = value;}public double volume(){

return length * width * height;}public String toString(){

return "Коробка"+ "Хпдлина = " + length + "; ширина = " + width + "; высота = " + height + "Хпобъем = " + volume();

1

Класс, описывающий манипулирование с коробками содержит один метод public void mult (Box box, double x). Этот метод умножает все три измерения коробки на один и то же коэффициент:public class BoxMaster {

public BoxMaster() {}public void mult (Box box, double x){

box.setLength(box.getLength() * x); box.setwidth(box.getWidth() * x); box.stHeight(box.getHeight() * x);

})

Page 120: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс BoxDemo демонстрирует, что метод mult объекта класса BoxMaser меняет свойства объекта Ы, переданного в метод mult в качестве фактического параметра:public class BoxDemo {

public static void main(String[] args) {// создаем мастера преобразующего коробочки BoxMaster master = new BoxMaster));// создам для него коробочку Box Ы = new Box(2, 3, 4);System.out.println("Создана " + Ы);// растянем коробочку master .mult (Ы, 2.);// поле преобразованияSystem.out.println{"После преобразования " + Ы);

;Результаты исполнения метода main демонстрируют

изменение свойств объекта, передаваемого в качестве параметра:Создана Коробкадлина = 2.0; ширина - 3.0; высота = 4.0 объем = 24.0

После преобразования Коробкадлина =• 4.0; ширина = 6.0; высота = 8.0объем = 192.0

О роли методовОчень часто, общаясь между собой, люди просят друг

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

Page 121: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

на ту, что происходит в мире информационных технологий. Объект «начальник» вызывает метод «устранить ошибку» объекта «программист». Аналогия может быть продолжена. Начальник не дает детальных инструкций программисту, как надо выполнять эту работу. Это прерогатива программиста - знать, как устранять выявленную ошибку. Он знает детально последовательность шагов выполнения порученной ему работы. Таким образом, начальник может не вникать во все подробности работы своего подчиненного, а решать задачи своего уровня. Точно так же в приведенном выше примере при вызове метода mult() мы не должны интересоваться, как же конкретно действует этот метод, чтобы решить поставленную передним задачу. Это позволяет нам сосредоточиться на решении других задач, связанных с управлением поведением объектов, не вникая в детали реализации отдельных методов этих объектов. В данном случае мы встречаемся с абстракцией описания поведения объектов. Мы вызываем метод, зная, ЧТО он должен делать, не задумываясь над тем, КАК он это делает.

Абстрагирование от реализации отдельных шагов поведения методов - важнейший момент в построении информационных моделей мира.

Page 122: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Массивы - линейные множества объектов.

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

Page 123: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Если массив содержит N элементов, то индекс может меняться от 0 до

N -1.Массив как объект имеет одно доступное для чтения

свойство length - количество элементов массива.Массив имеет специфический синтаксис описания:

<тип элементов> [] <имя массива>;Альтернативно массив можно описывать, размещая

квадратные скобки не перед именем массива, а после него:<тип элементов> <имя массива> [];

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

Например, массив целых чисел можно описать следующим образом:int [] а;

Так мы описали массив целых чисел. Абсолютно аналогичное описание будет иметь вид:int а [];

Создание массива так же, как и любого другого объекта выполняется с помощью оператора new, но конструктор выглядит тоже специфически. Например, создание описанного только что объекта будет выглядеть следующим образом:а = new int [10];

В приведенном примере создается экземпляр массива из 10 целых чисел.

Создание массива совершенно не означает, что обязательно создаются элементы массива. Если элементами массива являются переменные примитивных типов, то под них отводится место, и все они получают начальные значения. Числовые элементы получают значение 0, логические - значение false. Если же элементами массива

Page 124: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Так для описанного выше массива записьа [5] = 77;

будет означать, что элементу с номером 5 ( элементы нумеруются, начиная с 0) будет присвоено значение 77, а записьа [6] = а [5] + 10;

будет означать, что выберется значение элемента а[5], то есть 77 , к нему будет добавлено значение 10, и результат 87 будет присвоен элементу массива с номером 6.

В качестве первого примера работы с массивами построим простую программу, которая строит массив из 15 целых чисел и заполняет его элементами геометрической прогрессии с основанием 1 и знаменателем 2. Проект, демонстрирующий эту программу, называется GeometryProgress. И находится он в директории Chapter04:J * ★* <p>Title: Геометрическая прогрессия</р>★* <p>Description: Простейший пример, демонстрирующий* создание, заполнение и работу с массивом</р>* <p>Copyright: Copyright (с) 2009</р>** <р>Согорапу: </р>★* Sauthor not attributable* (Aversion 1.0 */

public class GeometryProgress {public static void main(String[] args) {

// описание и создание массива int [] gp = new int [15];// заполнение массива gp[0] = 1;

Page 125: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

for (int i = 1; i < gp.length; i++){ gp[i] = gp[i - 1] * 2;

}// распечатка содержиого массива for (int i = 0; i < gp.length; i++){

System.out.print(" ” + gp[i]);}

Запуск программы покажет, что на печать выводятся степени числа 2 с 0 по 14-ю включительно:1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384

Обратите внимание, что в программе не используется условие i < 15, а вместо этого в обоих циклахиспользовалется i < gp.length. Этот простой прием позволяет писать программы независимые от размера массива. Если понадобится изменить размер массива, то необходимо заменить в строкеint [] gp = new int[15];

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

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

Класс Box выглядит точно так же, как и в проекте Boxl, поэтому не будем повторно приводить его.

Класс ВохАггау приведен ниже:I ** <p>Title: Массив коробок </р>■** <p>Description: демонстрирует простейший вариант* создания, заполнения и использования массива* объектов</р>* <p>Copyright; Copyright (с) 2009</р>** <p>Company: МИИТ</р>★

Page 126: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* gauthor Михайлкж А.В.* gversion 1.0 */

public class ВохАггау {public static void main(String[] args) {

// описание и создание массива коробок Box [] boxes = new Box[15];// заполнение массива коробок коробками // случайного размераfor (int i = 0; i < boxes.length; i++){

boxes [i] = new Box(Math.random(),Math.random(),Math.random());

}// распечатка построенного массива for (int i = 0; i < boxes.length; i++){

System.out.println(i + ") " + boxes [i]);)

)}

Легко увидеть, что он очень похож на текст класса GeometryProgress. Это не случайное совпадение - оно показывает, что работа с массивами простых чисел и сложных объектов выполняется однотипно. Нижеприводится результат одного из запусков программы, который показывает результат построения массива объектов:0) Коробкадлина = 0.11355733484870467; ширина = 0.12243302536973999;

высота = 0.1660130463401881 объем = 0.0023081072831631351) Коробкадлина = 0.932963986970586; ширина = 0.6174726458045937;

высота = 0.027446195874954782 объем = 0.0158111974241197352) Коробкадлина = 0.16818020876084006; ширина = 0.45252707498391753;

высота = 0.3494047017833227 объем = 0.026591828454872273) Коробкадлина = 0.4247412330771846; ширина = 0.6872630396817242;

высота = 0.6437180234973071 объем = 0.18790705292919052

Page 127: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

длина = 0.4849318772090664; ширина = высота = 0.7478340601741849

объем = 0.255645010808355945) Коробкадлина = 0.13363215251745253; ширина =

высота = 0.15139969952694976 объем = 0.015939960484566976) Коробкадлина = 0.3931231883867563; ширина =

высота = 0.29619817945364535 объем = 0.0140745484935879947) Коробкадлина = 0.6999132904311327; ширина =

высота = 0.9750377807848183 объем = 0.29998870322156058) Коробкадлина = 0.15988518968830778; ширина =

высота = 0.5971369343151721 объем = 0.0261363672909896539) Коробкадлина = 0.4083182818913569; ширина =

высота = 0.8499132433480708 объем = 0.0351839598513135910) Коробкадлина = 0.14145589907841527; ширина =

высота = 0.7209615141361515 объем = 0.0749396559440425611) Коробкадлина = 0.7648013988162763; ширина =

высота = 0.4764471452876877 объем = 0.306491795401126712) Коробкадлина = 0.4009095457782391; ширина =

высота = 0.6404538652736296 объем = 0.0389249835170446313) Коробкадлина = 0.8486393235327448; ширина =

высота = 0.8163539417915939' объем = 0.6071277581977615

0.7049386890912958;

0.7878640119026721;

0.1208713646681524;

0.43958130734192247;

0.27375562646490936;

0.10138443719986556;

0.734815907320791;

0.8411151403243339;

0.15159825043697206;

0.876351720306543;

Page 128: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

длина = 0.6779663453443191; ширина = 0.46472543611610906;высота = 0.6350088652322928

объем = 0.20007110365306632

Основные операции на массивах. Сортировка и поиск.

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

В качестве типичных задач, решаемых для массивов, рассмотрим задачи сортировки поиска.

СортировкаЧто такое сортировка? Сортировкой называется

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

Page 129: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

В качестве критерия может выступать любая функция, которая имеет два аргумента, и отвечает на вопрос объект а < объекта b или а — b или а > Ь.

Обратите внимание, что нам абсолютно безразлична природа объектов. Для выполнения сравнения объектов важно только наличие средств сравнения.

Сортировка методом пузырькаРассмотрим самый распространенный, и, пожалуй,

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

Идея решения заключается в том, что надо по очереди ставить элементы на свое место, которое они должны занимать в отсортированном массиве. Массив из N элементов сортируется в N-1 этапов. На 1-м этапе на последнее место ставится самый большой элемент, и к нему больше не обращаются. На 2-м этапе на предпоследнее место ставится второй по величине элемент массива и так далее, на последнем этапе на позицию 1 (нумерация с нуля) ставится второй снизу элемент массива. После чего весь массив оказывается упорядоченным. При чем k-й этап состоит из последовательного сравнения пар соседних элементов массива, начиная с пары 0 - 1 и заканчивая парой (к - 1) - к. Если (к-1) оказывается больше k-го, эта пара элементов меняется местами. Таким образом, на k-м месте оказывается самый большой элемент из просмотренных от О до к элементов массива.I * ** <p>Title: Сортировка</р>★* <p>Description: Работа осуществляется в три этапа:

Page 130: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* 1. Создается и заполняется массив чисел с плавающей точкой

* 2. Сортируется методом пузырька* 3. выводится на печать результат сортировки** Для того чтобы подчеркнуть, что нам не важно,* как сравниваются элементы массива операция* сравнения вынесена в отдельный метод </р>** <p>Copyright: Copyright (с) 2009</р>★* <р>Сошрапу: МИИТ</р>* ©author Михайлюк А.В.* ©version 1.0 */

public class BulbSort (public static void main(String!] args) {,int SIZE = 10;double [] array = new double[SIZE];System.out.println("Строится массив:"); for (int i = 0; i < array.length; i++)(

array [i] = Math.randomO;System.out.println(i + ") " + array [i]);

}for (int k = array.length - 1; k > 0; k— ){

for (int j = 1; j <= k; j++){if (compare (arrayfj - 1], array (j]) > 0){

// если элемент (j - 1) > элемента [j],// то меняем их местами double x = array [j - lb- array [j - 1] = array [j]; array [j] = x;

}// в позиции [j] стоит элемент,// который >= любого из элементов // от (0] до. [j - 1]

}// в любой позиции [г], где I I k <= г <= array.length - 1// стоит элемент [г], кторый >= любого элемента [s], // где 0 <— s < г

}// в любой позиции [г], где 1 <= г <= array.length - 1 // стоит элемент [г], кторый >= любого элемента [s],// где 0 <* s < г. То есть массив упорядочен System.out.println("После сортировки:"); for (int i = 0; i < array.length; i++){

System.out.println(i + ") " + array [i]);}

}

Page 131: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

/*** compare сравнение чисел с двойной точностью* Если х > у, то результат > О* Если х < у, то результат < О* Если х == у, то результат = О** @param х double* Sparam у double */

private static double compare(double x, double y) { return x - y;

}

Ниже приводится текст результата одного из запусков этой программы:Строится массив:

0) 0.56540043935412811) 0.8173603243615772) 0.50391105429385993) 0.74743740914899944) 0.80757536945884825) 0.51404526378627446) 0.12575503015276297) 0.111449238795284348) 0.319881786266092549) 0.35345135349786005 После сортировки:0) 0.111449238795284341) 0.12575503015276292) 0.319881786266092543) 0.353451353497860054) 0.50391105429385995) 0.51404526378627446) 0.56540043935412817) 0.74743740914899948) 0.80757536945884829) 0.817360324361577

По поводу данного метода сделаем следующие замечания.

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

Page 132: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

2) Внесем в программу одно усовершенствование. Выделим алгоритм сортировки в отдельный метод. Проведем сравнение двух вариантов сортировки. Для этого применим к одинаковым массивам два разных варианта алгоритма сортировки. Массив возьмем достаточно большой, чтобы было видно, насколько заметным является наше усовершенствование.

3) Сравнивать будем по числу вызовов метода compare(), которые потребуется в обоих случаях. Сравнение методов сортировки по числу вызовов метода сошраге() является общепринятым, будем пользоваться им и впредь./*** <p>Title: Сортировка 2</р>★* <p>Description: Работа осуществляется в три этапа:* 1. Создается и заполняется массив* чисел с плавающей точкой* 2. строится копия массива* 3. Сортируется методом пузырька* 4. выводится на печать результат сортировки* 5. Сортируется улучшенным методом пузырька* 6. выводится на печать результат сортировки*

* Для того чтобы подчеркнуть, что нам не важно,* как сравниваются элементы массива операция* сравнения вынесена в отдельный метод </р>** Оба алгоритм сортировки тоже* вынесены в отдельные методы** Для сравнения результата сортировки одинаковых* массивов разными алгоритмами в обоих случаях* подсчитывается количество сравнений* элементов массива** <p>Copyright: Copyright (с) 2009</р>*

<p>Company: МИИТ</р>

Page 133: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* gauthor Михайлюк А.В.* @version 1.0 */

public class BulbSort {public static int counter; // счетчик числа раненийpublic static void main(String[] args) (

int SIZE = 100000;double [] array = new double[SIZE]; for (int i = 0; i < array.length; i++){

array [i] = Math.randomO;}//print("1-й массив после создания", array); double [] array2 = array.clone();//print("2-й массив после создания", array); counter = 0; bulbSort(array);System.out.println

("Количество сравнений по методу bulbSort "+ counter);

//print("1-й массив после сортировки", array); counter = 0; bulbSort2(array2);System.out.println

("Количество сравнений по методу bulbSort2 "+ counter);

//printC'2-й массив после сортировки", array2);

/*** compare сравнение чисел с двойной точностью* Если х > у, то результат > 0* Если х < у, то результат < 0* Если х == у, то результат = 0* @param х double* Sparam у double */

private static double compare(double x, double y) ( counter++; return x - y;

/*** print печать массива■к

* (Jparam title String* @param array doublet]*/

private static void print(String title, doublet] array) {

Page 134: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System.out.println(title) ; for (int i = 0 ; i < array.length; i++){

System.out.println(i + ") " + array [i]);

}

private static void bulbSort(double [] array)( for (int к = array.length - 1; к > 0; к--){

for (int j = 1; j <= k; j++){if (compare (array[j - 1], array [j]) > 0) {

// если элемент (j - 1) > элемента [j],// то меняем их местами double x = array [j - 1]; array [j - 1] = array [j]; array [j] = x;

}// в позиции [j] стоит элемент,// который >= любого из элементов // от [0] до [j - 1]

}I I в любой позиции [г],// где k <= г <= array.length - 1// стоит элемент [г],, кторый >= любого элемента [s], I I где 0 <— s < г

)// в любой позиции [г], где 1 <= г <= array.length - 1 // стоит элемент [г], кторый >= любого элемента [s],// где 0 <= s < г. То есть массив упорядочен

private static void bulbSort2(double [] array){ boolean cont = true;for (int k = array.length - 1; k > 0 && cont; k— ){

cont = false;for (int j = 1; j <= k; j++) {

if (compare (array[j - 1], array [j]) > 0) {// если элемент (j - 1) > элемента [j],// то меняем их местами double x = array [j - 1]; array [j - 1] = array [j]; array [j] = x; cont = true;

}// в позиции [j] стоит элемент,// который >= любого из элементов I I от [0] до [j - 1]

)// в любой позиции [г], где // k <= г <= array.length - 1// стоит элемент [г], кторый >= любого элемента [s],

Page 135: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// где 0 <= s < г}// в любой позиции [г], где 1 <= г <= array.length - 1 // стоит элемент [г], который >= любого элемента [s], I I где 0 <= s < г. То есть массив упорядочен

На массиве из 100 000 элементов программа дала следующий результат:Количество сравнений по методу bulbSort 704982704

Количество сравнений по методу bulbSort2 704920576

Мы видим, что 2-й алгоритм дал экономию по сравнению с 1 -м около 60 тысяч сравнений, но по сравнению с общим числом сравнений - около 705 миллионов - это незначительная экономия. Запуск программы на массиве такого размера уже занимает значительное время, и это должно побудить нас поискать более совершенные алгоритмы сортировки.

Быстрая сортировкаПолученный нами результат показывает, что метод

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

Попробуем проанализировать, почему метод пузырька так плох. Обратим внимание, что для того чтобы поставить на место n-й элемент массива, мы делаем п-1 сравнение элементов. Для (n-l)-ro п-2 сравнений всего, таким образом, мы должны выполнить

Page 136: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

(n - 1) + (n - 2) + (n - 3) + ... + 2 + 1 = (n - 1) * n / 2сравнений. Такое количество сравнений мы должны

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

Для этого посмотрим на процесс сортировки немного с иной точки зрения. Если массив а[0], а[1], ..., а[п-1] отсортирован, то для любого к, где 0 < к < п-1 справедливо, что для любого i, где 0 <= i < к выполнено отношение a[i] <= а[к], а для любого j, где к < j <= п-1 выполнено отношение а[к] <= аЦ]. Говоря простым языком, можно сказать, что в отсортированном массиве каждый элемент делит массив на две части: все стоящие слева от него элементы не больше его, а все стоящие справа от него элементы не меньше его. А что если взять неупорядоченный массив, выбрать в нем произвольный элемент и разделить исходный массив на части. Слева от выбранного элемента поставить все элементы, которые не больше выбранного элемента. Справа от него поставить все элементы, которые не меньше выбранного элемента. Пусть эти части массива пока сами не упорядочены, нам важно другое. Во-первых, мы таким образом сразу поставим выбранный элемент на его окончательное место, во-вторых, и это самое главное, элементы из левой части массива впредь никогда не потребуют сравнения с элементами из правой части массива. Таким способом мы уменьшим количество сравнений на последующих шагах. Если нам удалось разделить исходный массив на две непересекающиеся части, то надо посмотреть, что это за части. Если одна из частей не имеет ни одного элемента массива, то это означает, что мы взяли в качестве разделяющего элемента минимальный элемент и левая часть массива не существует. Если в левой части массива стоит один элемент, то он уже отсортирован, и его обрабатывать не нужно. Если же больше одного, то к этой части массива надо приметь аналогичную процедуру разделения на две части с помощью своего разделяющего элемента. С правой

Page 137: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

частью массива поступим точно также, если она содержит О или 1 элемент, то мы ее не трогаем, иначе применяем к ней ту же процедуру разделения массива на две части. Поскольку при каждом разделении массива, по крайней мере, один элемент становится на свое место, то длины разделяемых массивов неизбежно сокращаются при каждом разделении. Это гарантирует нам конечность процесса разбиения массива на подмассивы. Причем в среднем длина разделяемого массива будет сокращаться в два раза, а это означает, что глубина разбиения массива на подмассивы будет величиной порядка Iog2(n). При этом на каждом уровне разбиения будет выполнено порядка п сравнений. Следовательно, общее число сравнений будет оцениваться по формуле n*log2(n), которая растет значительно медленнее, чем приведенная выше формула п*(п-1)/2, выведенная нами для метода пузырька.

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

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

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

Page 138: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Понятие рекурсииКогда мы создавали наш алгоритм, то сказали

следующее: «Если в левой части массива стоит один элемент, то он уже отсортирован, и его обрабатывать не нужно. Если же больше одного, то к этой части массива надо применить аналогичную процедуру разделения на две части с помощью своего разделяющего элемента. С правой частью массива поступим точно также, если она содержит 0 или 1 элемент, то мы ее не трогаем, иначе применяем к ней ту же процедуру разделения массива на две части». То есть мы утверждаем, что если эта часть содержит 0 или 1 элемент, то ничего делать не нужно, а если больше одного, то к части массива надо применить тот же алгоритм, что и ко всему массиву. Такое описание алгоритма называется рекурсивным. Рекурсия - это описание алгоритма путем описания применения его ко всему множеству через его применение к частям множества. Фактически это говорит нам, что метод сортировки сам должен содержать в себе

Page 139: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

вызовы этого же метода сортировки. Такие методы называется рекурсивными. Как правило, рекурсивное описание алгоритма делает его (описание) более лаконичным и понятным. Обычно в качестве классического примера применения рекурсии приводят пример вычисления факториала:public int factorial (int x) {

if (x == 0) { return 1;

} else {return factorial (x - 1) * x;

Такое описание алгоритма в точности соответствует математическому определению факториала, но оно не имеет большой ценности, поскольку следующее определение ничуть не хуже, но исполняется более эффективно, чем рекурсивное:public int factorial (int x){

int result = 1; for (int i = 1; i <= x; i++){

result = result * i;}return result;

}Но в тех случаях, когда алгоритм носит ветвящийся

характер, применение рекурсии является естественным и неоценимым способом выражения программистской мысли.

Рекурсивный вызов метода опасен тем, что метод начнет вызывать себя неограниченное число раз, это приведет к зацикливанию программы и, в конце концов, к ее аварийному завершению. Поэтому при разработке рекурсивного алгоритма надо всегда следить за тем, чтобы по мере вызова рекурсивного метода, мы бы неуклонно приближались к выполнению условия окончания рекурсии.J к ** <p>Title: Сортировка</р>к* <p>Description: Работа осуществляется

Page 140: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* в несколько этапа:* 1. Создается и заполняется* массив чисел с плавающей точкой* 2. строится копия массива* 3. Сортируется методом пузырька* 4. выводится на печать результат сортировки* 5. Сортируется алгоритмом быстрой сортировки* 6. выводится на печать результат сортировки** Для того чтобы подчеркнуть, что нам не важно,* как сравниваются элементы массива операция* сравнения вынесена в отдельный метод </р>** Оба алгоритм сортировки тоже* вынесены в отдельные методы** Для сравнения результата сортировки одинаковых* массивов разными алгоритмами в обоих случаях* подсчитывается количество сравнений* элементов массива *■* <p>Copyright: Copyright (с) 2009</р>** <р>Сошрапу: МИИТ</р>■к* ©author Михайлюк А.В.* (Aversion 1.0 */

public class Quicksort {public static int counter; // счетчик числа раненийpublic static void main(String[] args) {

int SIZE = 100000;double [] array = new double[SIZE]; for (int i = 0; i < array,length; i++){

array [i] = Math.randomO;}//print("1-й массив после создания", array); double [] array2 = array.clone();//print("2-й массив после создания", array); counter = 0; bulbSort(array);System.out.println

("Количество сравнений по методу bulbSort "+ counter);

//printO'1-й массив после сортировки", array); counter = 0; quicksort(array2);System.out.println

("Количество сравнений по методу quicksort "+ counter);

Page 141: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

//print("2-й массив после сортировки", аггау2);

! ■* ** compare сравнение чисел с двойной точностью* Если х > у, то результат > О* Если х < у, то результат < О* Если х == у, то результат = О** @param х double* Йрагат у double */

private static double compare(double x, double y) { counter++; return x - y;

/*** print печать массива★* Sparam title String* Sparam array doublet]*/

private static void print(String title, doublet] array) {

System.out.println(title);for (int i = 0; i < array.length; i++){System.out.println

(i + ") " + array[i]);1

)private static void bulbSort(double [] array)!

for (int k = array.length - 1; k > 0; k--){ for (int j = 1; j <= k; j++){

if (compare (array [j - 1], array [j]) > 0) { // если элемент (j - 1) > элемента [j], //то меняем их местами double х = array [j - 1]; array [j - 1] = array [j]; array [j] = x;

}// в позиции [j] стоит элемент,// который >= любого из элементов // от [0] до [j - 1]

}// в любой позиции [г],// где k <= г <= array.length - 1 // стоит элемент [г],// который >= любого элемента [s],// где 0 <= s < г

Page 142: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

)// в любой позиции [г], где 1 <= г <= array.length -// стоит элемент [г], кторый >= любого элемента [s], // где 0 <= s < г. То есть массив упорядочен

private static void quicksort(double [] array){ quickSortl(array, 0, array.length - 1) ;

private static void quickSortl(double!] array, int low, int high)(

//printC'low = " + low + high = ” + high, array);int i, j;for (i = low, j = low + 1; j <= high; j++){

// i отслеживает позицию разделяющего элемента // j отслеживает анализируемую позицию if (compare (array [i] , array [j]) > 0)(

// если разделяющий// элемент больше анализируемого,// то его надо поставить слева //от разделяющего элемента exchange(array, i, j); i++;

}}if (i - low > 1) {

// если слева от разделяющего элемента // есть не меньше 2-х элементов, то нужна // сортировкаquickSortl(array, low, i - 1) ;

}if (high - i > 1) {

// если справа от разделяющего элемента // есть не меньше 2-х элементов, то нужна // сортировка

quickSortl(array, i + 1, high);}

}/ *** exchange** 0param array double]]* Sparam i int* @param j int*/

private static void exchange(double]] array, int i, int j) {

if (j - i == 1) {

Page 143: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// обмен соседних элементов double х = array [i]; array [i] = array [j];array [j] = x;

}else(// обмен далеко отстоящих друг от друга элементовdouble х = array [i + 1];array [i + 1] = array [i];array [i] = array [j];array [j] = x;

}Печать результатов при сортировке обоими методами массива

из 100 000 чисел с плавающей точкой дает впечатляющий . результат:

Количество сравнений по методу bulbSort 704982704 Количество сравнений по методу quicksort 2044905

Метод «пузырька» требует более чем в 350 раз больше сравнений, чем алгоритм quickSort()! Это говорит о том, что второй алгоритм гораздо эффективнее первого.

ПоискВторая классическая задача, решаемая на больших

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

Поиск последовательным переборомЕсли у нас нет дополнительной информации о том, как

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

Page 144: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Пример программы поиска случайного числа в массиве случайных целых приведен в проекте Search, расположенном в директории Chapter04:j * ** <p>Title: Поиск</р>★* <p>Description: В данной программе осуществляется* поиск элемента массива </р>*

* <p>Copyright: Copyright (с) 2009</р>★* <p>Company: </р>★* @author not attributable* Sversion 1.0*/

public class Search (private static final int SIZE = 20; private static int counter;

Page 145: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public static void main(String[] args) {// описание и создание массива целых чисел int [] array = new int [SIZE];// заполнение массива случайными элементами for (int i = 0; i < array.length; i++) {

array [i] = intRandom (SIZE);}// печать массива print (array);// задание образца для поиска int sample = intRandom(SIZE);System.out.println("Поиск элемента " + sample);// поиск элемета counter = 0;int result = search (array, sample);System.out.println("Количество сравнений " + counter) if (result == -1){

System.out.println("Элемент " + sample + " в массиве не найден");

} else {System.out.println("Индекс искомого элемента в "

+ "исходном массиве " + result);

J ★ ** print печать массива челых чисел;** Sparam array int[J */

private static void print(int[] array) { for (int i = 0; i < array.length; i++){

System, out .print (” ’’ + i + ") " + array[i]);}System.out.println();

j ** search последовательный поик элемента в массиве★* @param array int[] - массив, в котором* осуществляется поиск* @param sample int - поисковый образ или* искомый элемент массива* /

private static int search(int[] array, int sample) { for (int i = 0; i < array.length; i++){

if (compare(array[i],sample) == 0){ return i;

Page 146: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

return -1;)/*** compare★* Sparam i int* Sparam sample int* Sreturn boolean * /

private static int compare{int i, int sample) { counter ++; return i - sample;

)j ★ ★* intRandom построение случайного целого числа* равномерно распределенного в диапазоне от* 0 до SIZE - 1** Sparam SIZE int* Sreturn intV

private static int intRandom(int SIZE) { return (int)(Math.randomO * SIZE);

}

Результат одного из запусков программы выглядит следующим образом:0) 6 1) 1 2) 11 3) 3 4) 9 5) 1 6) 19 7) 19 8) 19 9) 15 10)

4 11) 12 12) 7 13) 16 14) 2 15) 4 16) 18 17) 8 18) 13 19) 14Поиск элемента 8 Количество сравнений 18Индекс искомого элемента в исходном массиве 17

Дихотомический поиск

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

Page 147: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

тех пор, пока не найдем искомый элемент или до тех пор, пока ни закончится массив.

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

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

Произвольно выберим любой элемент из этого массива для сравнения с искомым с искомым. Проведем сравнение, исходов может быть три :

1. Искомый элемент совпал с выбранным. В этом случае алгоритм свою работу закончил. Элемент найден, и надо выйти из программы, указав в качестве результата номер выоранного элемента.

2. Искомый элемент меньше выбранного. Тут еще ничего не ясно. Первое, что ясно сразу и безоговорочно это то, что искомый элемент не может находиться справа от выбранного. Там его искать не следует, поскольку массив упорядочен, а выбранный элемент строго больше искомого. Следовательно все, что находится справа от него, не может дать совпадения с искомым элементом. Поэтому искомый элемент может находиться только слева от выбранного элемента. А что находится слева? Тут опять же два варианта:

1) Разделяющий элемент был самым левым в массиве, следовательно, левее него ничего нет. Значит, искомого элемента нет в массиве

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

3. Искомый элемент больше разделяющего. В этом случае повторяется ситуация 2:

Page 148: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

1) Если справа от разделяющего элемента нет ни одного элемента, то это означает, что искомого элемента нет в массиве.

2) Справа от разделяющего элемента есть еще не исследованные элементы массива. В этом случае надо повторить попытку поиска в части массива, расположенной справа от разделяющего элемента.

Обратите внимание, что наш алгоритм изложен в форме рекурсивного алгоритма.

Единственный момент, который остался нераскрытым в нашем алгоритме, как выбирать разделяющий элемент. Если мы не знаем, по какому закону распределены числа в исходном массиве, то у нас нет предпочтения, какой элемент выбрать в качестве разделяющего. Поэтому наиболее предпочтительным выглядит выбор в качестве разделяющего элемент, находящийся в середине между точками left и right. Его номер легко рассчитать по формулеint middle = (left + right) / 2

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

Рассмотрим проект QuickSearch в директории Capter04:j -к ★

* <p>Title: Поиск 2</р>★* <p>Description: В данной программе осуществляется* построение массива случайных целых чисел,* затем упорядочим его методом пузырька,* построим случайное целое число, которое будем* использовать для поиска в массиве.* И применим два разных алгоритма поиска* к одним и тем же исходным данным.

Page 149: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Результат сравним по числу сравнений,* понадобившихся для поиска числа в массиве.</р>** <p>Copyright: Copyright (с) 2009</р>** <р>Сошрапу: </р>*•* Sauthor not attributable* (Aversion 1.0 */

public class QuickSerchDemo {private static final int SIZE = 20000; private static int counter;

public static void main(String[] args) {// описание и создание массива целых чисел int [] array = new int[SIZE];// заполнение массива случайными элементами for (int i = 0; i < array.length; i++){ array [i] = intRandom (SIZE);

>// сортировка массива sort(array);// печать массива //print (array);// задание образца для поиска int sample = intRandom(SIZE);System.out.println("Поиск элемента ” + sample); // поиск элемента counter = 0;int result = search (array, sample);System.out.println("Количество сравнений " +

counter);if (result *= -1)(System.out.println("Элемент " + sample

+ " в массиве не найден");} else {System.out.println("Индекс искомого элемента в

+ "исходном массиве " + result);)// быстрый поиск элемента counter = 0;result = quickSearch (array, sample);System.out.println("Количество сравнений " +

counter);if (result -= -1){System.out.println("Элемент " + sample

+ " в массиве не найден");} else {System.out.println("Индекс искомого элемента в

+ "исходном массиве " + result);

Page 150: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

j * ** quickSearch быстрый поиск элемента массива★* Sparam array int[]* Sparam sample int* Sreturn int */

private static int quickSearch (int[] array, int sample) (

return quickSearchl(array, sample, 0, array.length - 1);

j * ★* quickSearchl рекурсивный алгоритм поиска* элемента в массиве** Sparam array int[]* Sparam sample int* Sparam i int* Sparam il int* Sreturn int */

private static int quickSearchl(int[] array, int sample, int left, int right) {

int midle = (right + left) / 2;/*System.out.println(

"left = " + left + right = " + right+ midle = " + midle

) ;*/int comparision = compare (array [midle], sample); if (comparision == 0){

return midle;) else if (comparision > 0){ if (midle == left)(

return -1;}else{return quickSearchl(array, sample, left, midle -

1) ;}

} else {if (midle == right)(

return -1;}else{return quickSearchl(array, sample, midle + 1,

right);}

Page 151: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

}f k k

* sort сортировка массива целых чисел** Sparam array int[]*/

private static void sort(int[] array) 1for (int k = array, length - 1; k >= 1; k— ) •{

for (int i = 1; i <= k; i++)(if (compare(array[i - 1], arrayti]) > 0){ int x = array[i - 1]; array [i - 1] = array [i]; array [i] = x;

}}

}j k k

* print печать массива целых чисел;к

* Sparam array int[]*/

private static void print (int[] array) { for (int i = 0; i < array.length; i++)(System.out.print (" " + i + ") " + array[i]);

}System.out.println();

}J к к

* search последовательный поиск элемента в массивек

* Sparam array int [] - массив, в котором* осуществляется поиск* Sparam sample int - поисковый образ или* искомый элемент массива */

private static int search(int[] array, int sample) { for (int i = 0; i < array.length; i++){ if (compare(array[i],sample) == 0){ return i;

return -1;)J к к

compare

Page 152: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Sparam i int* Sparam sample int* Sreturn boolean V

private static int compare(int i, int sample) { counter ++; return i - sample;

j * ★* intRandom построение случайного целого числа* равномерно распределенного в диапазоне от* 0 до SIZE - 1** Sparam SIZE int* Sreturn int */

private static int intRandom(int SIZE) { return (int)(Math.random() * SIZE);

}

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

Количество сравнений 4083Индекс искомого элемента в исходном массиве 4082 Количество сравнений 13Индекс искомого элемента в исходном массиве 4082

Другие запуски этой программы дают похожий результат. Вы видите, что количество сравнений для поиска одного элемента последовательным перебором и методом деления массива пополам на массиве из 20 000 чисел дает результаты в тысячи раз лучшие для метода деления массива пополам. Найти элемент массива из 20 000 чисел всего за 13 или 14 сравнений очень хорошо. Это серьезный результат. Однако продолжим наши исследования в части улучшения алгоритма поиска.

Page 153: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Наиболее слабым местом в алгоритме быстрого поиска является определение разделяющего элемента. Мы пошли по простейшему пути: всегда делим массив на две примерно равные части независимо от того, какое число мы ищем. Именно это обстоятельство объясняет, что для поиска любого числа нам всегда надо примерно 1 3 - 1 4 сравнений. Давайте сосредоточимся на правиле поиска разделяющего элемента.

Что нам известно о свойствах массива? Он заполнен случайными целыми числами в диапазоне от 0 до SIZE - I. В частном случае SIZE = 20000, и он упорядочен. Случайные числа распределены равномерно в указанном диапазоне. Опираясь на значения элементов массива в краях диапазона и зная искомое значение sample, мы можем выбрать разделяющий элемент так, чтобы он делил отношение (middle - left) / (right - left) в той же пропорции, что (sample - array[left]) / (array [right] - array [left]). Указанные соотношения станут более понятными, если посмотреть на связи между элементами массива и искомым числом на рис. 15.

Рис. 15. графическое пояснение к способу вычисления разделяющего элемента.

Если написать фрагмент метода, вычисляющий положение разделяющего элемента на языке

Page 154: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

программирования, то получится следующий фрагмент метода:

if (array [right] ==* array [left]) {middle = (right + left) / 2;

}else{double к = ((double) sample - array [left])

/ (array[right] - array[left]);if (k < 0){ к = 0;

}else if (k > 1){ к = 1;

}middle = left + (int)((right - left) * k);

}Проанализируем этот фрагмент подробно.Для чего нужно условие if (array [right] == array [left])?

He нужно забывать, что числа, заполняющие массив случайные и вполне может сложиться такая ситуация, что все элементы в подмассиве array[left], ..., array[right] окажутся равными друг другу. Тогда приведенные нами рассужденья не работают, и какой элемент выбрать в качестве разделяющего безразлично. Выбираем средний, хотя с таким же успехом могли бы взять и любой другой.

Для чего в оператореdouble k = ((double) sample - array [left])

/ (array[right] - array[left]);использовано явное преобразование типов данных от

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

Для чего нужно условиеif (к < 0){ к = 0;

}else if (к > 1){

Page 155: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

к = 1;

Рассмотрим случай, когда массив состоит всего из пяти чисел и заполнен значениями 3, 3, 4, 5, а искомое число 2. В этом случае коэффициент к окажется меньше нуля. Это будет означать, что нам надо выбрать разделяющий элемент левее левого, что невозможно. Аналогично может сложиться ситуация, что коэффициент к окажется больше 1. Это будет означать, что мы должны выбрать разделяющий элемент правее правого, что тоже невозможно. Поэтому мы вынуждены отсекать эти случаи, присваивая крайние значения коэффициенту к, если он выходит за допустимые границы.

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

Полный проект с модифицированным алгоритмом быстрого поиска Вы найдете в директории Chapter04 проект Search3. Не станем приводить полный текст проекта, поскольку он в значительной части повторяет проект Search2. приведем только сам метод быстрого поиска и его вызов:

/*** quickSearch2 модифицированный метод быстрого поиска** Sparam array int[] упорядоченный массив целых чисел* Sparam sample int искомый элемент масива* Sreturn int*/

private static int quickSearch2(int[] array, int sample){return quickSearch21

(array, sample, 0, array.length - 1);}/*** Quicksearch21 - рекурсивный алгоритм быстрого поиска *

* Sparam array int[] - масив, в котором

Page 156: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* осуществляется поиск* Sparam sample int - искомый элемент* Sparam left int - левая граница поиска* Sparam right int - правя граница поиска* Sreturn int - номер элемента в массиве */

private static int quickSearch21(int[] array, int sample, int left, int right) {

int middle;if (array [right] == array [left]){ middle = (right + left) / 2;

}else(double k = ((double) sample - array [left])

/ (array[right] - array[left]); if (k < 0){ k = 0;

)else if (k > 1){ k = 1;

}System.out.print(

"sample = " + sample + array[left] = " + array [left]+ array[right] = " + array [right]+ "; k = " + k + "; ") ;

middle = left + (int) ( (right - left) * k);}System.out.println("left = " + left + "; right = " + right + "; middle = " + middle

) ;int comparision = compare (array [middle], sample) if (comparision == 0){ return middle;

} else if (comparision > 0){ if (middle == left){ return -1;

}else(return quickSearch21

(array, sample, left, middle - 1);}

} else {if (middle == right){ return -1;

}else{return quickSearch21

(array, sample, middle + 1, right);}

}}

Page 157: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Приведем также результаты одного из запусков программы:Поиск элемента 17540

Количество сравнений 17619Индекс искомого элемента в исходном массиве 17618 Количество сравнений 12Индекс искомого элемента в исходном массиве 17620 sample = 17540; array[left] = С; arrayfright] = 19999; k = 0.8770438521926096; left = 0; right = 19999; middle = 17540sample = 17540; arrayfleft] = 17461; array[right] = 19999; k = 0.031126871552403467; left = 17541; right = 19999; middle = 17617sample = 17540; array[left] = 17540; array[right] = 19999; k = 0.0; left = 17618; right = 19999; middle = 17618Количество сравнений 3Индекс искомого элемента в исходном массиве 17618

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

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

Page 158: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

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

Page 159: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

него, он ли последний в очереди? Когда наш покупатель ответит утвердительно, вновь подошедший станет последним в очереди. Наш покупатель станет ждать своей очереди в кассу, зная, что за ним есть, по крайней мере, еще один покупатель. Для чего подошедший к очереди задает стоящему последним человеку вопрос, ответ на который он знает почти наверняка? Это нужно для того, чтобы между предыдущим и последующими членами очереди установилась информационная связь. Подошедший человек запоминает предыдущего. Предыдущий запоминает последующего. Это позволяет людям не стоять в линеечку один за другим, а спокойно дожидаться своей очереди, зная, кто будет обслужен перед ним и кто будет обслужен после него. Особенно наглядно это видно на очереди к врачу, где больные дожидаются своей очереди, сидя на стульях перед кабинетом. И порядок их расположения на стульях чаще всего не совпадает с их порядком в очереди. Таким образом, еще одним важным свойством очереди является то, что ее элементы располагаются в произвольном порядке, а порядок следования элементов очереди поддерживается за счет установления информационных связей между ее элементами.

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

ОчередьОчередь характеризуется тем, что добавляются

элементы в хвост, а извлекаются элементы из головы. Такая дисциплина называется FIFO - сокращение от английских

Page 160: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

слов First In First Out - первым пришел, первым обслужись. Структура такой очереди очень проста.

Назовем класс, порождающий объекты, управляющие очередями Queue. Объект, управляющий очередью должен содержать ссылки на первый и последний элементы очереди. Если ссылка на первый элемент очереди пустая, то это может служить надежным признаком, что очередь пуста. Конструктор должен создавать пустую очередь. Очередь должна реализовывать два метода:public void put(Item item); public Item get();

Первый метод добавляет новый метод в хвост очереди.Второй метод извлекает из головы очереди первый

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

Для поддержания очереди в актуальном состоянии достаточно в каждом элементе очереди иметь ссылку на следующий элемент очереди. Назовем это свойство next - следующий. У последнего элемента очереди эта ссылка будет пустой. Каждый элемент очереди должен содержать информацию, характеризующую элемент очереди. В наших примерах это будет целое число. Назовем это свойство value. Для однозначной идентификации элементов очереди введем свойство объекта класса Item, которое назовем id - целое число, однозначно идентифицирующее элементы очереди. Оно будет присваиваться каждому элементу очереди автоматически в момент его создания.

Рассмотрим проект Queue, расположенный в директории Chapter04. Он состоит из трех классов: Item, Queue и QueueDemo. Начнем с рассмотрения класса Item. Он описывает класс объектов, образующих элементы очереди целых чисел. Это очень простой класс. Он имеет несколько приватных свойств, простой конструктор и методы доступа к свойствам:/*** <p>Title: Элемент очереди </р>

Page 161: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* <p>Description: Элемент очереди, содержащий:* - ссылку на следующий элемент или null* - уникальный идентификатор элемента* - значение элемента очереди </р>** <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р>•** ^author not attributable* (Aversion 1,0 */

public class Item {private Item next; // ссылка на след элемент очереди private int id; // уникальный идентификатор private int value; // значение элемента очереди// счетчик элементов очереди private static int counter = 0;// конструктор public Item(int value) {

this.value = value; id = ++counter; next = null;

}// группа методов доступа к свойствам объекта public Item getNextО (

return next;}public void setNext (Item newNext){

next = newNext;)public int getValue(){

return value;

public int getld()( return id;

}public String toString()(

return ""+ id + ") "+ value;

)1

Page 162: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс Queue тоже не сложный. Он имеет два приватных свойства - указатели на начало и конец очереди, простой конструктор и пару методов. Один метод public void put (Item item) - помещает элемент в конец списка, другой pulic Item get () - выдает в качестве результата первый элемент из очереди.j * ★* <p>Title: Очередь </р>•к* <p>Description: </р>* <p>Copyright: Copyright (с) 2009</р>** <р>Сошрапу: </р>** ©author not attributable* ©version 1.0 V

public class Queue {private Item first, last;public Queued {

first = null; last = null;

}// добавление элемента в очередь // item - добавляемый элемент очереди public void put(Item item){

// если очередь пуста, то if (first == null){

// добавляемый элемент становится первым first = item;// и последним last = item;

}else{// иначе// после последнего элемента устанавливается // добавляемый last.setNext(item);// последним становится добавляемый last = item;/ / и в нем устанавливается признак,// что он последний last.setNext(null);

1)

Page 163: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// получение элемента очереди public Item get() throws Exception (

// если очередь пуста, то if (first == null){

// возбудить исключительную ситуацию throw new Exception("Попытка взять элемент "

+ "из пустой очереди");}// результат - первый элемент из очереди Item result = first;// первым становится элемент, на который до этого // указывал первый first = first.getNextО ;// если первый содержал null, то if (first == null){

// это означает, что очередь стала пустой //и последний элемент тоже ни на //что не указывает last = null;

}return result;

public String toStringOf String result; if (first == null){

result = "Очередь пуста";}else{ result =for (Item curr = first; curr != null; curr = curr.getNext0){ result += (curr == first ? : "; ")

+ curr.toString();}

}return result;

Класс QueueDemo, как следует из названия, демонстрирует основные приемы работы с очередью. Он создает очередь, помещает в нее пять элементов, потом берет из очереди три элемента, потом добавляет еще пять и после этого пытается взять из нее 10 элементов, но десяти в очереди нет, их только 8, и при попытке взять девятый элемент возникает исключительная ситуация, которая перехватывается и обрабатывается.j * *

Page 164: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* <p>Title: </p>** <p>Description: </p>* <p>Copyright: Copyright (c) 2009</p>** <p>Company: </p>** Sauthor not attributable* (Aversion 1.0 */

public class QueueDemo (public static void main(String[] args) {

Queue queue = new Queue () ;System.out.println("Создана очередь " + queue); for (int i = 0; i < 5; i++){

Item item = new Item(intRandom(100));System.out.println("добавить элемент 11

+ item);queue.put(item);

}System.out.println("Очередь после добавления ”

+ "5 элементов: "+ queue

) ;try{

for (int i = 0; i < 3; i++){Item item = queue.get ();System.out.println("Получен элемент очереди

+ item

)System.out.println("Очередь после удаления "

+ "3-х элементов: "+ queue);

System.out.println("Добавим еще 3 элемента"); for (int i = 0; i < 3; i++){

Item item = new Item(intRandom(100)); System.out.println("добавить элемент "

+ item);queue.put(item);

)System.out.println("Очередь после добавления

+ "3-х элементов: "+ queue

) ;System.out.println("Попытка получить 10 "

+ "элементов из очереди"); for (int i = 0; i < 10; i++){

Item item = queue.getO;System.out.println("Получен элемент очереди

Page 165: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

)+ item

}catch(Exception e){System.out.println("Перехвачена исключительная

+ "ситуация: "+ e.getMessage()

) ;}

(/*** intRandom вырабатывает случайное целое число* в диапазоне от 0 до i - 1** Sparam i int - дапазон случайных чисел* Sreturn int */

private static int intRandom(int i) { return (int)(Math.random() * i);

)

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

добавить элемент 1) 14 добавить элемент 2) 40 добавить элемент 3) 80 добавить элемент 4) 40 добавить элемент 5) 2Очередь после добавления 5 элементов: 1) 14; 2) 40; 3)80; 4) 40; 5) 2Получен элемент очереди 1) 14Получен элемент очереди 2) 40Получен элемент очереди 3) 80Очередь после удаления 3-х элементов: 4) 40; 5) 2Добавим еще 3 элементадобавить элемент 6) 51добавить элемент 7) 13добавить элемент 8) 67Очередь после добавления 3-х элементов: 4) 40; 5) 2; 6) 51; 7) 13; 8) 67Попытка получить 10 элементов из очередиПолучен элемент очереди 4) 40Получен элемент очереди 5) 2Получен элемент очереди 6) 51Получен элемент очереди 7) 13Получен элемент очереди 8) 67

Page 166: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Перехвачена исключительная ситуация: Попытка взять элемент из пустой очереди

Упорядоченная очередьВ повседневной жизни мы часто сталкиваемся с

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

Будем считать, что определение, какой элемент больше, какой меньше выполняет специальный объект, который принадлежит классу Comparator и реализует один важный метод public int compare (Item il, Item i2). Этот метод возвращает значение большее нуля, если i 1 > i2, ноль, если il = i2 и значение меньшее нуля, если il < i2. При этом нам абсолютно безразлично, как происходит сравнение. Это скрыто в реализации метода compare(). Важно только то, что у нас есть возможность сравнить любые два элемента очереди.

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

Page 167: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс Item полностью повторяет аналогичный класс в проекте Queue, поэтому его не приводим.

Класс Comparator новый по сравнению с проектом Queue, поэтому приведем его целиком.j к к

* <p>Title: Comparator</p>к

* <p>Description: объекты этого класса выполняют* сравнение двух элементов списка</р>* <p>Copyright: Copyright (с) 2009</р>•к* <p>Company: </р>к

* ^author not attributable* Oversion 1.0 */

public class Comparator ( public Comparator() {1public int compare(Item il, Item i2){

return il.getValue() - i2.getValue();1

}Класс SortedQueue очень похож на класс Queuee.

Содержит следующие отличия:1. В качестве свойства добавлен объект класса

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

2. Метод public void put (Item newltem) заменен на public void insert (Item newltem), который включает элементы в список в соответствии с их упорядоченностью.

Текст этого класса приведен ниже:j -к к

* <p>Title: Sorted Queue</p>** <p>Description: реализует очередь элементов,* упорядоченных в соответствии с некоторым* критерием</р>к *

* <p>Copyright: Copyright (с) 2009</р>

Page 168: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* <p>Company: МИИТ</р>•к* ©author Михайлюк А.В.* ©version 1.0 */

public class SortedQueue {private Comparator comparator; private Item first, last;public SortedQueue(Comparator comp) f

comparator = comp; first = null; last. = null;

}// добавление элемента в очередь в соответствии // критерием упорядочения public void insert(Item newltem){

if (first == null){ first = newltem; last = newltem;

}else{Item prev = null; // предыдущий Item curr = first;// текущий // поиск места в списке while (curr != null &&

// поиск идет либо до конца списка comparator. compare (newltem, curr) >= 0.) { // либо до тех пор, пока новый // больше текущего prev = curr;// предыдущим становится текущий curr = curr.getNextО ;// текущим становится

}if (curr == null){

// вставка в конец списка last.setNext (newltem);// новый идет за последним newltem.setNext(null);// за новым никого last = newltem;// новый становится последним

Jelse if (prev == null){// вставка в начало списка newltem.setNext(curr);//за новым следует текущий first = newltem;// новый становится первым

}else{// вставка в середину списка

Page 169: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

prev.setNext(newltem);// новый становится за предыдущим newltem.setNext(curr);// за новым ставится текущий

}}

}// получение элемента очереди public Item get() throws Exception {

// если очередь пуста, то if (first == null){

// возбудить исключительную ситуацию throw new Exception("Попытка взять эелемент

+ "из пустой очереди");}// результат - первый элемент из очереди Item result = first;// первым становится элемент,// на который до этого // указывал первый first = first.getNext ();// если первый содержал null, то if (first == null){

// это означает, что очередь стала пустой // и последний элемент тоже ни на //что не указывает last = null;

}return result;

public String toString(){String result; if (first == null){

result = "Очередь пуста";)else{ result =for (Item curr = first; curr != null;

curr = curr.getNext()){ result += (curr == first ? "" : "; ")

+ curr.toString();}

}return result;

})

Класс Demo проекта SortedQueue повторяет класс QueueDemo проекта Queue за исключением того, что вызов

Page 170: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

метода put() регулярно заменен вызовом метода insert(). Поэтом не будем его приводить здесь, а вот результаты одного из запусков программы приведем.Создана очередь Очередь пуста

добавить элемент 1) 87 добавить элемент 2) 9 добавить элемент 3) 98 добавить элемент 4) 42 добавить элемент 5) 26Очередь после добавления 5 элементов: 2) 9; 5) 26; 4)42; 1) 87; 3) 98 Получен элемент очереди 2) 9 Получен элемент очереди 5) 26 Получен элемент очереди 4) 42Очередь после удаления 3-х элементов: 1) 87; 3) 98Добавим еще 3 элементадобавить элемент 6) 69добавить элемент 7) 21добавить элемент 8) 12Очередь после добавления 3-х элементов: 8) 12; 7) 21;6) 69; 1) 87; 3) 98Попытка получить 10 элементов из очередиПолучен элемент очереди 8) 12Получен элемент очереди 7) 21Получен элемент очереди 6) 69Получен элемент очереди 1) 87Получен элемент очереди 3) 98Перехвачена исключительная ситуация: Попытка взять элемент из пустой очереди

СтекЕще одной часто используемой структурой данных,

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

Page 171: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

использован патрон, первым заложенный в магазин. Такая дисциплина обслуживания называется LIFO - Last In First Out (последним вошел - первым обслужись).

В программировании дисциплина LIFO встречается не реже, чем дисциплина FIFO. Поэтому будет поучительно построить пример, демонстрирующий работу со стеком. Этот пример находится в проекте Stack в директории Chapter04. Он построен на базе проекта Queue, поэтому не будем приводить его целиком, как и в проекте SortedQueue приведем лишь классы, существенно отличающиеся от аналогичных классов в проекте Queue.

Класс Item ничем не отличается от аналогичного класса в проекте Queue, поэтому его мы не приводим.

Класс Stack приведем полностью:J *■* <p>Title: Стек</р>** <p>Description: Реализует управление стеком.* Для поддержания стека используются 2 метода* - push(), который помещает новый элементов в* вершину стека* - pop(), который выбирает из стека элемент,* находящийся в вершине стека</р>*•* <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р>★* @author not attributable* (Aversion 1.0 */

public class Stack { private Item top;public Stack!) {

top = null;

j * i* push помещает новый элементов вершину стека** Sparam item Item */

public void push(Item item) {

Page 172: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

item.setNext(top); top = item;

j * ** pop доставляет в качестве результата* элемент, находящийся в вершине стека* Если стек пуст, то возбуждается* исключительная ситуация, информирующая* о попытке получения данных из пустого стека */

public Item pop() throws Exception { if (top == null){

throw new Exception("Попытка получения "+ "данных из пустого стека");

}Item result = top; top = top.getNext(); return result;

}public String toString(){

String result; if (top == null){

result = "Очередь пуста";}else{ result =for (Item curr = top; curr != null;

curr = curr.getNext()){ result += (curr == top ? "" : "; ")

+ curr.toString();1

}return result;

}}

Класс Demo тоже приведем, хотя он очень мало отличается от QueueDemo:j -к *

* <p>Title: </р>** <p>Description: </р>★* <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р>** Sauthor not attributable* Aversion 1.0

Page 173: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

*/public class Demo {

public static void main(String[] args) {Stack stack = new Stack();System.out.println("Создан стек " + stack); for (int i = 0; i < 5; i++){

Item item = new Item(intRandom(100));System.out.println("добавить элемент "

+ item);stack.push(item);

}System.out.println("Стек после добавления "

+ "5 элементов: "+ stack

) ;try{

for (int i = 0; i < 3; i++){Item item = stack.popO;System.out.println("Получена вершина стека

System.out.println("Стек после удаления "+ "3-х элементов: "+ stack);

System.out.println("Добавим еще 3 элемента")for (int i = 0; i < 3; i++){

Item item = new Item(intRandom(100));System.out.println("добавить элемент "

+ item);stack.push(item);

}System.out.println("Стек после добавления "

+ "3-х элементов: "+ stack

) ;System.out.println("Попытка получить 10 "

+ "элементов из Стека"); for (int i = 0; i < 10; i++)(Item item = stack.popO;System.out.println("Получена вершина стека

+ itemif

) ;}

(catch(Exception e){System.out.println("Перехвачена исключительная

+ "ситуация: "+ e .getMessage()

it

}

Page 174: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

/*** intRandom вырабатывает случайное целое число* в диапазоне от 0 до i - 1** Sparam i int - диапазон случайных чисел* Sreturn int */

private static int intRandom(int i) { return (int)(Math.random() * i);

}}

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

добавить элемент 1) 17 добавить элемент 2) 13 добавить элемент 3) 17 добавить элемент 4) 99 добавить элемент 5) 28Стек после добавления 5 элементов: 5) 28; 4) 99; 3) 17; 2) 13; 1) 17Получена вершина стека 5) 28 Получена вершина стека 4) 99 Получена вершина стека 3) 17Стек после удаления 3-х элементов: 2) 13; 1) 17Добавим еше 3 элементадобавить элемент 6) 17добавить элемент 7) 99добавить элемент .8) 7 6Стек после добавления 3-х элементов: 8) 76; 7) 99; 6)17; 2) 13; 1) 17Попытка получить 10 элементов из Стека Получена вершина стека 8) 76 Получена вершина стека 7) 99 Получена вершина стека 6) 17 Получена вершина стека 2) 13 Получена вершина стека 1) 17Перехвачена исключительная ситуация: Попытка получения данных из пустого стека

Списки, как средства представления множествРассмотренные в

упорядоченные очереди разновидности одного

настоящей главе очереди, и стеки представляют собой широкого класса структур,

Page 175: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Одним из таких применений является применение списков для представления множеств. Множества образуют объекты произвольного типа. Для простоты ограничимся рассмотрением множеств целых чисел. Множества характеризуются тем, что каждый элемент может входить в него не более одного раза. Все элементы, входящие во множество, доступны для чтения или удаления из множества. По своей природе множества -неупорядоченные совокупности элементов. Но мы уже видели выше, что операции над упорядоченными массивами идут гораздо быстрее, чем над неупорядоченными. Аналогичная ситуация наблюдается и со списками. Поэтому будем представлять множества упорядоченными списками элементов. Рассмотрим работу с упорядоченными множествами на примере проекта SortedSet. Он состоит из четырех классов: Item, Comparator, SortedSet и Demo. Классы Item и Comparator не отличаются от аналогичных классов, описанных в предыдущих проектах. Поэтому мы приводить их не будем.

Класс SortedSet приведен ниже:/ * *** <p>Title: Sorted Set</p>★* <p>Description: Поддерживает упорядоченное множество* целых чисел</р>** <p>Ccpyright: Copyright (с) 2009</р>** <p>Company: МИИТ</р>★* @author Михайлюк А.В.* (Aversion 1.0*/

public class SortedSet {

Page 176: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

private Comparator comparator; private Item first;public SortedSet(Comparator comp) {

comparator = comp; first = null;

}// добавление элемента в очередь в соответствии // критерием упорядочения public void include(Item newltem))

if (first -= null)( first = newltem;

}else{Item prev = null; // предыдущийItem curr = first;// текущийint comparision =0; // результат сравнения// нового элемента с текущим// поиск места в спискеwhile (curr != null &&

// поиск идет либо до конца списка (comparision =comparator.compare(newltem, curr)) > 0) (

// либо до тех пор, пока новый // больше текущего prev = curr;// предыдущим становится текущий curr = curr.getNext();// текущим становится

if (curr == null){// новый идет за последним newltem.setNext(null);// новый становится последним prev.setNext(newltem);

}else if (comparision == 0){// если элемент уже входит во множество,// то добавлять его не нужноreturn;

1if (prev == null){

// вставка в начало списка newltem.setNext(curr);// за новым следует текущий first = newltem;// новый становится первым

}else{// вставка в середину списка prev.setNext(newltem);// новый становится за предыдущим newltem.setNext(curr);

Page 177: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

/ / за новым ставится текущий

}

public String toString(){String result; if (first == null){

result = "Очередь пуста";}else{ result =for (Item curr = first; curr != null;

curr = curr.getNext())( result += (curr == first ? "" : ")

+ curr.toString();

}return result;

/ * ** isElement Отвечает на вопрос, включено ли* число в список** 0param item Item */

public boolean isElement(Item item) { if (first == null)(

return false;}Item prev = null; // предыдущийItem curr = first;// текущийint comparision = 0; // результат сравнения// нового элемента с текущим// поиск места в спискеwhile (curr != null &&

// поиск идет либо до конца списка(comparision = comparator.compare(item, curr)) > 0){ // либо до тех пор, пока новый // больше текущего prev = curr;// предыдущим становится текущий curr = curr.getNext();// текущим становится следующий

1// выходим из цикла по одному или двум следующим // условиям:// - дошли до конца цикла // - очередной элемент >= текущего // возвращаем истину только, если текущий // элемент равен искомому

Page 178: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

return comparision == 0;

// добавление в конец списка элемента, который // точно больше всех предыдущих private void append (Item item){

if (first == null){ first = item;

}else{Item curr = first;I I поиск последнего элемента в списке while (curr.getNext() != null){

curr = curr.getNext();}// включение нового элемента вслед // за последним curr.setNext(item);

}item.setNext(null) ;

// Объединение множеств.//в результирующее множество включаются // элементы, входящие хотя бы в одно из двух // множествpublic SortedSet union (SortedSet set) {

// построение результирующего множества SortedSet result = new SortedSet(comparator);// результат сравнения двух элементов // из двух множеств int comparision = 0;I I описание элементов списков, пробегающих по I I по 1-му и 2-му спискам Item left = this.first, right = set.first; while (left != null && right != null){

// сравнит пару элементов из обоих списков comparision = comparator.compare(left, right); if (comparision < 0){

I I если левый элемент меньше правого,//то включить его и продвинуться // по левому спискуresult.append(new Item(left.getValue())); left = left.getNext();

) else {// иначе взять правый элемент и продвинуться // по правому спискуresult.append(new Item(right.getValue()) ) ; right = right.getNext();// если элементы равны, то продвинуться // и по левому списку if (comparision == 0)(

Page 179: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

left = left.getNext();

// левая ветвь кончилась, докопривать правую if (left == null){

while (right != null){result.append(new Item(right.getValue() ) ) ; right = right.getNext();

}// правая ветвь кончилась, докопировать левую if (right == null){

while (left != null){result.append(new Item (left.getValue())) ; left = left.getNext();

return result;

// Пересечение множеств./ / в результирующее множество включаются те и // только те элементы, которые входят в оба // множестваpublic SortedSet intersect(SortedSet set)(

// построение результирующего множества SortedSet result = new SortedSet(comparator);// результат сравнения двух элементов // из двух множеств int comparision = 0;// описание элементов списков, пробегающих по //по 1-му и 2-му спискамItem left = this.first, right = set.first; while (left != null && right != null){

// сравнит пару элементов из обоих списков comparision = comparator.compare(left, right) if (comparision < 0)(•.// если левый элемент меньше правого,/7 то включить его и продвинуться // по левому списку left = left.getNext();

} else if (comparision > 0) {// иначе если левый элемент больше правого //то продвинуться // по правому списку right = right.getNext();

) else {// если элементы равны, то включить // элемент в результатresult.append(new Item(left.getValue()));

Page 180: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// и продвинуться по обоим спискам left = left.getNextО ; right = right.getNextО ;

return result;}J ★ ★* differ построение разности двух множеств,* то есть множества, состоящего из тех элементов* первого множества, которые не входят во второе** Йрагаш set SortedSet*/

public SortedSet differ(SortedSet set) {// построение результирующего множества SortedSet result = new SortedSet(comparator);// результат сравнения двух элементов // из двух множеств int coraparision = 0;// описание элементов списков, пробегающих по // по 1-му и 2-му спискамItem left = this.first, right = set.first; while (left ! = null && right != null){

// сравнит пару элементов из обоих списков comparision = comparator.compare(left, right) if (comparision < 0){

// левый элемент меньше правого,// следовательно, в правый уже никогда //не войдет. Значит он наш. result.append(new Item(left.getValue())); left = left.getNext();

} else {// если правый меньше или равен левого,// то продвинуть правый список right = right.getNext();// если правый равен левому, то он не // входит в разность множеств if (comparision == 0){

// продвинуться и по левому множеству left = left. getNext О;

// если в левом списке еще остались элементы while (left != null)(

// то их все надо включить в результат result.append(new Item(left.getValue())); left = left.getNext();

Page 181: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

return result;

Приемы работы с множествами можно проследить в классе Demo:/*** <p>Title: Demo </р>** <p>Description: Демонстрирует работу со* списками, представляющие множества целых чисел* </р>** <p>Copyright: Copyright (с) 2009</р>* <p>Company: МИИТ</р>* @author Михайлюк А.В.* 0version 1.0 */

public class Demo {public static void main(String[] args) {

Comparator comparator = new Comparator();SortedSet setl = new SortedSet(comparator); for (int i = 0; i < 10; i++){

setl.include(new Item(intRandom(20)));}System.out.println("set 1 = " + setl);SortedSet set2 = new SortedSet(comparator); for (int i = 0; i < 10; i++){

set2.include(new Item(intRandom(20)));}System.out.println("set 2 = " + set2);System.out.println("Объединение множеств");SortedSet union = setl.union(set2);System.out.println{"union = " + union);SortedSet intersection = setl.intersect(set2);System.out.println{"Пересечение множеств");System.out.println("intersection = " + intersection); SortedSet difference = setl.differ(set2);System.out.println("Разность множеств");System.out.println("diff = " + difference);

j ★* intRandom Получение случайного целого* равномерно распределенного в диапазоне* от 0 до i-1 *• ** @param i int - диапазон случайных чисел

Page 182: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Sreturn int * /

private static int intRandom(int i) { return (int)(Math.randomO * i);

}}

Рассмотрим результаты одного из запусков программы:set 1 = 0; 2; 4; 7; 8; 10; 17; 19

set 2 = 2; 3; 5; 7; 14; 15; 16Объединение множествunion = 0; 2; 3; 4; 5; 7; 8; 10; 14; 15; 16; 17; 19Пересечение множествintersection = 2; 7Разность множествdiff = 0; 4; 8; 10; 17; 19

Сортировка списковСписки - очень популярные в программировании

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

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

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

Алгоритм быстрой сортировки списков тоже точно повторяет логику быстрой сортировки массивов. Для пустых

Page 183: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

В приводимом примере в одной программе Demo используются оба алгоритма сортировки. Для того чтобы можно было сравнить применение разных алгоритмов к одинаковым исходным данным, разработан метод public List сору(), который строит точную копию того списка, к которому он применен.

Рассмотрим проект SortList, расположенный в директории Chapter04. Он состоит из четырех классов: Item, Comparator, List и Demo.

Класс Item описывает элементы списка. Он очень похож на одноименный класс из предыдущих проектов, но отличается от них наличием ссылки не только на следующий элемент списка, но и на предыдущий. Это позволяет осуществлять перемещения по списку, как от начала к концу, так и от конца к началу. Ниже приводится текст этого класса:j ★* <p>Title: Элемент очереди </р>★* <p>Description: Элемент списка, содержащий:* - ссылку на следующий элемент или null* - ссылку на предыдущий элемент или null* - уникальный идентификатор элемента* - значение элемента очереди </р>

Page 184: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* <p>Copyright: Copyright (c) 2009</p>■Jr

* <p>Companyr </p>** Sauthor not attributable* (Aversion 1.0 */

public class Item {private Item next; // ссылка на след элемент списка private Item prev; // ссылка на предыдущий элемент private int id; // уникальный идентификатор private int value; // значение элемента очереди// счетчик элементов очереди private static int counter = 0;// конструктор public Itemfint value) 1

this.value = value; id = ++counter; next = null;

}// группа методов доступа к свойствам объекта public Item getNext(){

return next;}public void setNext (Item newNext){

next = newNext;

public Item getPrev(){ return prev;

}public void setPrev (Item item)

prev = item;}public int getValue(){

return value;

public int getld() return id;

public String toStringOl return ""

Page 185: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

+ id + ") "+ value;

)

Класс Comparator похож на одноименный класс из предыдущих проектов, но отличается дополненной возможностью подсчета числа сравнений. Для этого он дополнен статической переменной private static int counter. Каждое сравнение увеличивает его значение на 1. Для управления этой переменной добавлены два статических метода: 1) public static void setCounter(), который сбрасывает счетчик сравнений в ноль, и 2) public static int getCounterQ, который доставляет текущее значение счетчика сравнений. /*** <p>Title: Comparator</p>к* <p>Description: объекты этого класса выполняют* сравнение двух элементов списка</р>** <p>Copyright: Copyright (с) 2009</р>** <p>Company: МИИТ</р>** Sauthor Михайлюк А.В.* Sversion 1.0 */

public class Comparator {private static int counter = 0;public Comparator() {

public int compare(Item il, Item i2){ counter ++;return il.getValue() - i2.getValue();

public static void setCounter(){ counter = 0;

public static int getCounter(){ return counter;

}

Page 186: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс List содержит в себе всю логику управления списком.

Он содержит два свойства first и last, предназначенные для указания на первый и последний элементы списка.

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

Разберем два метода public void append(Item item) и private void append(List list) . Первый из этих методов добавляет элемент item в конец текущего списка. Второй добавляет целый список list в конец текущего списка. Здесь мы наблюдаем, что два различных метода выполняют похожие действия, имеют одинаковое имя и разные списки параметров. Такие методы называются перегруженными. Для того, чтобы можно было однозначно определить, какой метод должен исполняться в каждом конкретном случае, они должны отличаться количеством и/или типом параметров. Количество и типы параметров называются сигнатурой метода. Перегруженные методы должны отличаться друг от друга сигнатурой. Появление перегруженных методов может вызвать недоумение. Зачем нужно разные методы называть одинаковыми именами. Многие языки программирования прекрасно обходятся без перегруженных методов. Однако это очень полезное и удобное свойство. Если методы выполняют похожие действия с разными аргументами, то, назвав их одним и тем же именем, мы показываем, что мы так и задумывали - выполнить похожие действия с разными объектами. В нашем случае - это добавление в конец списка. В одном случае добавляется один элемент, в другом - целый список. Это позволяет нам абстрагироваться от реализации, а сосредоточиться только на результатах.

Метод public List сору() создает новый список, являющийся точной копией текущего списка. Новый список содержит другие элементы, но с геми же значениями и в том же порядке, что и текущий.

Page 187: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Метод public void bulbSort() осуществляет сортировку списка методом пузырька. Алгоритм сортировки является точным переложением на язык списков аналогичного алгоритма, написанного нами для массивов, но порядок перестановки элементов массива довольно запутанный из-за того, что надо переключить большое число ссылок на элементы списка. А именно: сами переставляемые элементы имеют ссылки друг не друга. Элементы, стоящие перед ними и после них, если не первый и не последний элементы списка; указатели first и last, если это первый и\или последний элементы списка; указатель на конец проверяемой подцепочки, если это последний элемент в проверяемом подсписке. Все это делает простой алгоритм сортировки методом пузырька довольно сложным в реализации.

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

Ниже приводится текст этого классаJ ★ ** <p>Title: Список</р>★* <p>Description: Данный класс описывает поведение* списков, предназначенных для хранения элементов,* имеющих ссылку на следующий элемент. Для удобства* Список хранит указатели на начало и конец списка,* хотя это и не обязательно. Достаточно хранить* только ссылку на начало списка. </р>к* <p>Copyr.ight: Copyright (с) 2009</р>■к* <р>Сошрапу: </р>

Page 188: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Sauthor Михайлюк А.В.* (aversion 1.0 */

public class List (private Comparator comparator; private Item first = null, last = null;// конструктор списка.// в качестве единственного параметра ему I I передается объект, осуществляющий сравнение // двух элементов списка /// / в момент создания список пуст/ /public List(Comparator comparator) {

this.comparator = comparator;}// формирует строку с содержимым списка public String toString(){

String result; if (first == null){result = "Список пуст";

)else{ result =for (Item curr = first; curr \ - null;

curr = curr.getNext())( result += (curr == first ? "" : "; ")

+ curr.toString();}

}return result;

}j * ★* append добавление элемента в конец списка★* @param item Item */

public void append(Item item) { if (first == null){

first = item;}else{last.setNext(item);

}item.setPrev(last); last = item; item.setNext(null);

Page 189: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

j * ** bulbSort сортировка списка методом пузырька */

public void bulbSort() {// внешний цикл// в нем в позицию limit ставится самый // большой элемент списка из оставшихся // с начала списка for (Item limit = last;

limit != first; limit = limit.getPrev()){

// внутренний цикл// в нем мы проходим по списку элементов //и переменная curr всегда указывает на // наибольший элемент с начала списка до // позиции currfor (Item curr = first.getNext(), prev = first;

prev != limit;prev = curr, curr = curr.getNext()) {

// если предыдущий элемент оказался больше // текущего, то их надо поменять местами if (comparator.compare(prev, curr) > 0){

//System.out.println("swap " + prev // + " and " + curr);

// если левый элемент из двух - начало // спискаif (prev == first){

// то второй становится первым в списке first = curr;

}else{// Иначе элемент идущий перед левым // должен указывать на больший элемент prev.getPrev().setNext(curr);

}// если правый элемент последний в списке, if (curr == last){

// то последним становится левый last = prev;

)else{// иначе элемент, ссылающийся на // правый должен ссылаться на левый curr.getNext().setPrev(prev);

)// если правый элемент последний //из рассматриваемых if (curr == limit){

// то последним становится левый элемент limit = prev;

}// перестроение ссылок в самих элементах // левому предшествует то,

Page 190: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// что до этого предшествовало левому curr.setPrev(prev.getPrev());// левому теперь предшествует правый prev.setPrev(curr);// За левым теперь следует то,// что раньше следовало за правым prev.setNext(curr.getNext ());// за правым теперь следует левый curr.setNext(prev);// левый и правый меняются местами curr = curr.getNext (); prev = prev.getPrev();//System.out.println("После перестановки

// + this);}//System.out.println("limit = " + limit);

/*** copy создает точную копию текущего списка */

public List copy() {List result = new List(comparator); for (Item curr = first;

curr != null; curr = curr.getNext ()){

result.append(new Item(curr.getValue()));)return result;

/*** Quicksort быстрая сортировка.*/

public void quicksort() {if (first == null || first == last){

return;}List left = new List(comparator);List right = new List(comparator);Item separator = first;Item curr = first.getNext(); while (curr != null){

Item next = curr.getNext();if (comparator.compare(separator, curr) > 0){

left.append(curr);)else(right.append(curr);

)

Page 191: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

curr = next;}left.quicksort(); right.quicksort(); left.append(separator); left.append(right); first = left.first; last = left.last;

* append - добавление списка list к текущему* списку * /

private void append(List list) { if (first == null)1

first = list.first; last = list.last;

}else(if (list.first == null)!

return;1last.setNext(list.first); list.first.setPrev(last); last = list.last;

}Класс Demo демонстрирует работу со списками. Он

создает список, заполняет его случайными элементами, создает копию списка, сортирует оригинальный список методом пузырька, копию списка - методом быстрой сортировки. Выводит на печать исходные списки, отсортированные списки и количество сравнений, выполненных во время каждой из сортировок. Текст класса приведен ниже:I ★ ** <p>Title: Demo</p>•к* <p>Description: Демонстрирует создание* и сортировку списков</р>** <p>Copyright: Copyright (с) 2009</р>* <р>Сошрапу: МИИТ</р>** Sauthor Михайлюк А.В.

Page 192: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Sversion 1.0 */

public class Demo {public static void main(String[] args) {

List listl = new List (new Comparator)));System.out.println("Создан список: " + listl); for (int i = 0; i < 10; i++){

listl.append(new Item (intRandom (100)));}System.out.println("Заполненный список: "

+ listl);List list2 = listl.copy();System.out.println("Копия списка: " + list2);Comparator.setCounter(); listl.bulbSort () ;System.out.println("Количество сравнений "

+ Comparator.getCounter()) ;System.out.println("Отсортированный список: "

+ listl);Comparator.setCounter() ; list2.quicksort () ;System.out.println("Количество сравнений "

+ Comparator.getCounter());System.out.println("Быстрая сортировка: " + list2);

j "k +* intRandom вырабатывает случайное целое число* в диапазоне от 0 до i - 1** Sparam i int -дапазон случайных чисел* Sreturn int */

private static int intRandom(int i) { return (int)(Math.randomO * i);

))

Результат одного из запусков программы для списка из 10 элементов приведен ниже:Создан список: Список пуст

Заполненный список: 1) 7; 2) 75; 3) 27; 4) 46; 5) 0; 6) 59; 7) 56; 8) 15; 9) 14; 10) 57Копия списка: 11) 7; 12) 75; 13) 27; 14) 46; 15) 0;16) 59; 17) 56; 18) 15; 19) 14; 20) 57 Количество сравнений 45Отсортированный список: 5) 0; 1) 7; 9) 14; 8) 15; 3)27; 4) 46; 7) 56; 10) 57; 6) 59; 2) 75 Количество сравнений 29Быстрая сортировка: 15) 0; 11) 7; 19) 14; 18) 15; 13)

Page 193: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

27; 14) 46; 17) 56; 20) 57; 16) 59; 12) 75

Небольшая модификация текста этого класса позволяет провести вычислительный эксперимент, запустив программу на списках разной длины. Ниже приведены результаты запкска программ на списках длинной 10, 100, 1000 и 10000 элементов.

Количество элементов списка

Сортировкаметодомпузырька

Быстраясортировка

10 45 19

100 4 950 700

1 000 499 500 11 409

10 000 49 995 000 150 825

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

Деревья - отражение иерархической связи между объектами реального мира

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

Page 194: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Несколько армий образуют фронт или в мирное время военный округ. Несколько округов образуют вооруженные силы государства. На каждом уровне управления командир не общается со всей массой своих подчиненных, а только с теми, кто ему непосредственно подчинен. Командир отделения командует семью - девятью солдатами. Командир взвода - тремя командирами отделений, командир ты - тремя командирами взводов и так далее до Главнокомандующего, который отдает свои команды командующим фронтами или округами. Такое построение позволяет мобильно и точно управлять огромными массами людей, согласованно направлять из действия на достижение стратегических целей. Такой управленческий эффект достигается за счет того, что количество подчиненных на каждом уровне иерархии увеличивается в геометрической прогрессии. В описанной структуре всего восемь уровней иерархии: 1) отделение, 2) взвод, 3)рота, 4) батальон, 5) полк, 6) дивизия, 7) армия, 8) Вооруженные Силы. На каждом уровне управления у каждого начальника обозримое количество подчиненных, с которыми он непосредственно общается. За счет этого команды должны доходить до подчиненных быстро и не подвергаться большим искажениям, которые неизбежны, если они передаются через длинную цепочку людей.

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

Page 195: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Рис. 16. Одиноко стоящее дерево- На нем прекрасновидно, как оно ветвится,

Page 196: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Деревья, которые рисуют программисты, растут «корнем» вверх. Вершина, из которой растет дерево, традиционно называется «корень» (root). Точки ветвления называются «вершинами», Ветки, соединяющие вершины, называются «ребрами». Вершины, которые стоят в конце веток называются «листьями». Ребра, соединяющие вершины - четко ориентированны, то есть имеют начало и конец. В каждую вершину может входить не более одного ребра. Вершина, в которую не входит ни одного ребра называется «корнем». В дереве может быть только один корень. Вершины, из которых не исходит ни одного ребра называются «листьями». Если из каждой вершины исходит не более двух ребер, то такое дерево называется «двоичным» деревом. На рис. 17 нарисовано дерево

Рис. 17. Дерево. 1, 2,..., 14 - вершины. 1 - корень. 3, 8, 9, 10, 12, 13 и 14 - листья. 1-2, 1-3, 1-4, 2-5, 2-6, 4-7, 4-8, 5-9, 6-10, 7-12, 7-13, 7-14-ребра.

Page 197: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Каждая вершина дерева должна иметь следующую структуру:

• Ссылки на подчиненные вершины. В случае двоичных деревьев их обычно называют left и right. Если подчиненных вершин больше, то их обычно собирают в массив. Если вершина - лист, то все ее ссылки на подчиненные вершины равны null;

• Ссылку на вышестоящую вершину. Обычно она называется parent. Для корня дерева эта ссылка равна null, для остальных вершин она заполнена;

• Идентификатор вершины - уникальное целое число, однозначно идентифицирующее вершину.

• Содержимое вершины. Это может быть число или объект произвольной природы.

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

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

1. левое ребро либо пусто, либо ссылается на вершину не большую текущей вершины;

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

Page 198: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рассмотрим базовые алгоритмы работы супорядоченными двоичными деревьями. Для этогообратимся к проекту BinaryTree в директории Chapter04.

Проект состоит из четырех классов:• Node - класс, описывающий отдельную вершину

дерева. Он содержит свойства, конструктор и методы доступа к этим свойствам. Свойствами вершины являются

о Node left -указатель на левую подчиненнуювершину или null, если такой вершины нет;

о Node right - указатель на правуюподчиненную вершину или null, если такой вершины нет;

о Node parent - указатель на вышестоящуювершину в дереве или null, если вершина является корнем дерева

о int id - уникальный идентификатор вершиныво множестве всех верши дерева. Свойство доступно только для чтения

о int value - значение вершины.Устанавливается в момент создания вершины. Свойство доступно только для чтения.

• Comparator - вспомогательный класс, содержащий один метод public int compare(Node left, Node right), выполняющий сравнение двух вершин дерева. Мы ввели его для того, чтобы отделить правило сравнения вершин дерева от самого дерева

• Tree — основной класс, описывающий поведение двоичных упорядоченных деревьев. Он содержит следующие свойства:

о Node root - корень дерева или null дляпустого дерева

о Comparator comparator - объект,выполняющий сравнение вершин дерева.

Page 199: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Кроме того, он содержит конструктор. Конструктор в качестве параметра получает объект класса Comparator, который и запоминает в качестве одного из своих свойств.

Класс Tree описание методов, определяющих порядок работы с двоичными упорядоченными деревьями:

о public void insert(Node node) - вставка вдерево новой вершины. Новая вершина всегда добавляется в дерево, как лист. Место для ее вставки определяется так. чтобы не нарушить упорядоченность дерева.

о public void delete(Node node) - удалениевершины из дерева. Этот алгоритм сложнее алгоритма вставки, поскольку удаляемая вершина может оказаться не только листом, но и вершиной, имеющей одну или две подчиненные вершины. В этом случае алгоритм удачен и я должен сработать так, чтобы при удалении не нарушилась упорядоченность дерева.

о public Node find (int value) - поиск вершиныдерева с заданным значением. Поиск осуществляется от корня путем поочередного сравнения вершин дерева с заданным значением. Если очередное сравнение показывает совпадение значения вершины с заданным значением, то результат найден и поиск прекращается. Если искомое значение меньше рассматриваемого, то происходит движение вниз по дереву на один уровень и на нем повторяется сравнение искомой вершины. Если движение вниз влево невозможно, то эго говорит о том, что вершина с указанным значением в дереве отсутствует. Аналогичное движение вниз вправо выполняется, если искомое значение больше текущей вершины.

• Comparator содержит один метод public int compare (Node left, Node right), который выполняет сравнение значений двух вершин дерева и возвращает значение большее нуля, если left больше, чем right; значение меньшее нуля, если left меньше, чем right и нуль, если они равны.

Page 200: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

• Demo создает дерево, заполняет его случайными целыми значениями. После чего делает несколько попыток поиска разных чисел в дереве. Каждый раз, как в дереве находится вершина, она удаляется из дерева. Результаты всех шагов выводятся на печать.

Текст проекта BinaryTree приводится ниже./*** <p>Title:Node </р>** <p>Description: Описывает вершину двоичного дерева</р>■** <p>Copyright: Copyright (с) 2009</р>■к* <р>Сошрапу: МИИТ</р>** @author Михайлюк А.В.* Inversion 1.0 */

public class Node {private Node parent = null, left = null, right = null; private int id; private int value;private static int counter = 0;/ / конструктор public Node(int value) {

id = ++counter; this.value = value;

}// серия методов доступа к свойствам вершин public Node getParent{){

return parent;}public void setParent(Node node){

parent = node;

public Node getLeft(){ return left;

public void setLeft(Node node){ left = node;

}

Page 201: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public Node getRight()( return right;

}public void setRight(Node node)(

right = node;

public int getld (){ return id;

public int getValue(){ return value;

public String toString(){ return id + ") " + value;

)! *- ** clear очистка ссылок вершиныV

public void clear() {parent = null; left = null; right = null;

/*** <p>Title: Comparator</p>■k* <p>Description: объекты этого класса выполняют* сравнение двух вершин дерева</р>■к* <p>Copyright; Copyright (с) 2009</р>** <р>Сошрапу: </р>к* @author not attributable* (Aversion 1.0 */

public class Comparator {private static int counter = 0;public Comparator() {}public int compare(Node il, Node i2){

Page 202: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

counter++;return il.getValue() - i2.getValue();

public static void setCounter(){ counter = 0;

}public static int getCounter(){

return counter;

j * ** <p>Title: Дерево</р>** <p>Description: Описывает двоичное* упорядоченное дерево</р>** <p>Copyright: Copyright (с) 2009</p>★* <p>Company: МИИТ </p>■к* Sauthor Михайлюк А.В.* Sversion 1.0 */

public class Tree {private Comparator comparator; private Node root = null;public Tree(Comparator comparator) {

this.comparator = comparator;

public void insert (Node node){ if (root == null)(

// вставка в корень дерева root = node;// присвоить корню значение новой вершины node.setParent(null) ;// родительской вершины тоже нет return;

)Node curr = root;while (true){ // цикл вечный потому, что выход

// будет из тела циклif (comparator.compare(curr, node) > 0) {

// если текущая вершина больше новой if (curr.getLeft() == null) (

// если ссыли влево нет, то нашли место // для добавляемой вершины

Page 203: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

curr.setLeft(node);// новая вершина становится левой для текшей node.setParent(curr);// текущая становится родительской для новой return;

}// иначе продвинемся вниз по дереву влево curr = curr.getLeft();

}else {// новая вершина больше или равна текущей

if (curr.getRight() == null) {// если справа внизу вершины нет, то //мы нашли место для новой вершины curr.setRight(node);// новая вершина становится вправо вниз// от текущейnode.setРаrent(curr);// текущая вершина становится родительской// для новойreturn;

)curr = curr.getRight();// иначе продвигаемся по дереву вниз и вправо

}}

}public String toStringО {

if (root == null){return "Дерево пусто";

}String result = toString (root, ""); return result;

/*** toString рекурсивный метод формирования* текстового представления двоичного дерева** Sparam root Node* Sparam i int* Sreturn String */

private String toString(Node node, String level) { String result = if (node.getLeft() != null){

result += toString(node.getLeft(),level + "L")+ "\n";

)result += level + " " + node.toString(); if (node.getRight () != null){

Page 204: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

result += "\n"+ toString(node.getRight(), level + "R")

}return result;

! -k -k* find Найти в дереве вершину с указанным* значением** Sparam anlnt int - искомое значение */

public Node find(int anlnt) (Node node = new Node(anlnt);Node curr = root; int comparision ; while (curr != null

&& (comparision= comparator.compare(curr, node)) != 0)

if (comparision > 0){ curr = curr.getLeft();

}else{curr = curr.getRight();

}if (curr != null){)

)return curr;

/*** delete удаление из дерева указанной вершины*

* Sparam node Node - удаляемая вершина */

public void delete(Node node) { if (node.getLeft () == null) (

// справа ничего нет if (node.getRight() == null){

// и слева ничего нет - лист if (node.getParent() == null){

// это корень root = null;

}else{// лист, но не кореньNode parent = node.getParent();if (parent.getLeft() == node){

// левый лист parent.setLeft(null);

}else(// правый лист

Page 205: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

parent.setRight(null);}

}}else{// есть продожение вправо if (node.getParent() == null){

// удаление корня root = node.getRight();Node right = node.getRight(); right.setParent(null) ; node.clear();

}else{// удаление из середины Node parent = node.getParent (); Node right = node.getRight(); if (parent.getLeft() == node){ parent.setLeft(right);

}else{parent.setRight(right);

!right.setParent(parent);

}}else(// ссылка влево не пустая if (node.getRight() == null){

// ссылка вправо пустая if (node.getParent () == null){-

// удаляем корень дерева Node left = node.getLeft(); root = left; left.setParent(null);

}else{// удаляем вершину, у кторой только// левая ссылкаNode left = node.getLeft();Node parent = node.getParent() ; if (parent.getLeft(! == node){ parent.setLeft(left);

)else{parent.setRight (left);

)left.setParent(parent);

)}else(// У удаляемой вершины обе ссылки не пустые Node left = node.getLeft();Node right = node.getRight();Node parent = node.getParent(); if (parent == null)!

// удаляем корень

Page 206: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

root = left;root.setParent(null) ;

}else{if (parent.getLeft () == node){ parent.setLeft (left);

}else(parent.setRight(3 eft);

)left.setParent(parent);

)insert(right);

}}node.clear();

f * ** <p>Title: Demo</p>•** <p>Description; Демонстрация работы с* упорядоченным двоичным деревом</р>* <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р>** Sauthor not attributable* Oversion 1.0 */

public class Demo {•public static void main(String[] args) {

int SIZE = 100; int DIAPAZON = 200;Tree tree = new Tree(new Comparator!));System.out.println("Построено дерево. " + tree); for (int i = 0; i < SIZE; i++){

tree.insert(new Node(intRandom(DIAPAZON)));)System.out.println("Дерево заполнено "

+ "\n" + tree);for (int i = 0; i < SIZE; i++){Comparator.setCounter(); int j = intRandom(DIAPAZON);System.out.print("Поиск вершины со значением "

+ j ) :Node result = tree.find(j); if (result == null){

System.out.println(". Вершина не найдена "+ "counter = " + Comparator.getCounter())

)else(

Page 207: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System, out .println (". Вершина: " + result+ ” counter = " + Comparator.getCounter()

tree.delete(result);//System.out.println("После удаления: "

//"\n" + tree //} ;

}System.out.println("После всех удалений:

+ "\n" + tree);

/ Ыг ** intRandom вырабатывает случайное целое число* в диапазоне от 0 до i - 1** Sparam i int - дапазон случайных чисел* Sreturn int * /

private static int intRandom(int i) { return (int)(Math.random() * i);

}

Приведем результат одного из запусков программы:Построено дерево. Дерево пусто

Дерево заполнено LL 10) 2 L 2) 3 LR 3) 3 LRRL 9) 4 LRR 8) 6

1) 7 RL 6) 9 RLR 7) 14 R 4) 18 RR 5) 19

Поиск вершины со значением 15. Вершина не найдена counter = 4Поиск вершины со значением 8. Вершина не найдена counter = 3Поиск вершины со значением 18. Вершина: 4) 18 counter = 2Поиск вершины со значением 3. Вершина: 2) 3 counter = 2 Поиск вершины со значением 8. Вершина не найдена counter = 2Поиск вершины со значением 0. Вершина не найдена counter = 2

Page 208: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Поиск вершины counter = 5

СО значением 5. Вершина не найденаПоиск вершины со значением 4 . Вершина:: 9) 4 counterПоиск вершины counter = 4

со значением 4 . Вершина не найденаПоиск вершины со значением 0. Вершина не найденаcounter = 2 После всех удалений:

L 10) 2 LR 3) 3 LRR 8) 6

1) 7 R 6) 9 RR 7) 14 RRR 5) 19

Page 209: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 5. Ошибки в программных системах и пути борьбы с ними

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

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

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

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

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

Page 210: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Обратимся к мнению замечательного писателя Сергея Лукьяненко. В своей статье «Апостолы инструмента», посвященной анализу труда писателя, он пишет:

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

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

А потом книга выходит, и оказывается, что графиню в одном месте называют баронессой — и она ничуть не возмущается. И еще находится десять грубых смысловых ошибок, которые «бета-тестинг» не отловил. Мала была группа экспертов? Да нет, просто все увлеклись. Потому что «вылавливать блох» — профессия. Этим занимается (ну или должен заниматься в идеале) редактор — человек не просто эрудированный и грамотный, но и не позволяющий себе увлечься текстом. Ляпы и ошибки, увы, неизбежны. Здесь можно приводить массу хрестоматийных примеров — и «ЭнциклопУдию», выпущенную без единой опечатки, и Робинзона Крузо, приплывшего голышом на разбитый корабль и тут же набившего сухарями карманы, и того героя Вальтера Скотта, что скакал весь день и всю ночь между деревушками, расположенными в десяти милях друг от друга.»

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

Page 211: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Проанализируем основные причины возникновения ошибок.

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

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

Page 212: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

Таким образом, сопровождение программногопродукта часто вносит в него новые ошибки.

Page 213: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Повторное использование кода - один из путей создания надежного программного продукта

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

Если существует программа, которая надежно решает нужную вам задачу - используйте ее.

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

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

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

Page 214: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

задача решалась в системе один раз, а обращение к решению этой задачи шло из разных точек создаваемой системы.

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

Очень часто в процессе сопровождения программист повторно пишет модули решающие одни и те же задачи. Это говорит о том, что процесс сопровождения плохо организован.

Это не только приводит к ненужному дублированию кода, но чревато появлением трудно уловимых ошибок. Предположим, что в силу слабого руководства разработкой или сопровождением программы в системе появились две реализации одной и той же функции f(x). И пусть обе они правильные. Система работает нормально. В ходе сопровождения программы потребовалось изменить алгоритм вычисления этой функции. Программист, выполняющий сопровождение системы знает об одной реализации этой функции, вносит в нее изменения. Проверяет их на соответствие новым требованиям и убеждается, что теперь функция f(x) работает в соответствии с новыми требованиями и работает правильно. Однако система престает работать! Раньше она честно работала, вычисляя f(x) в соответствии со старыми требованиями, а теперь она вообще не работает, потому, что в части мест вызывается старая реализация, в части мест - новая. Поймать такую ошибку очень трудно. Надо, во-первых, выявить все места в системе, где происходит обращение к вычислению этой функции, во-вторых, понять, что при этом вызываются разные реализации одной и той же функции. Устранение этой ошибки должно сводиться к устранению ненужного и опасного дублирования кода.

Общее правило выглядит следующим образом:Каждая функция в системе должна

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

Page 215: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Следование этому правилу сократит объем кодирования, сделает вашу программу более устойчивой к ошибкам и более легкой в сопровождении.

Абстракция - основной путь преодоления проблемы усложнения программных комплексов

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

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

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

Решение проблемы заключается в разумной декомпозиции взаимодействия объектов системы.

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

Page 216: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Вывод из сказанного выше следующий:Не пытайтесь решить большую задачу сразу и

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

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

Объектно-ориентированный подход - современный способ борьбы с нарастанием сложности программного обеспечения

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

Page 217: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Page 218: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 6. Повторное использование кода - первый метод борьбы со сложностью

Порядок хранения , компиляции и исполнения программ на языке Java

Мы уже говорили о том, что программы хранятся в исходном виде в обычных текстовых файлах с расширением .java. Теперь поговорим о том, как происходит трансляция и исполнение программ.

Для трансляции программы необходимо вызвать программу java.exe и передать ей в командной строке имя файла, содержащего исходный текст программы и некоторые параметры, о которых мы поговорим ниже. В результате трансляции будут сформированы файлы с именами, совпадающими с именами классов и расширение .class. Эти файлы являются двоичными кодами специальной машины, получившей название JVM - Java Virtual Machine (виртуальная Java-машина). По замыслу авторов языка не предполагалось трансляции текстов программ на языке Java в машинные коды конкретного вычислителя. Предполагалось наличие некоторого интерпретатора кодов виртуальной машины на конкретном процессоре с конкретной операционной системой. Такое решение позволяло достичь нескольких целей.

Первой целью было достижение реальной переносимости программ из одной среды в другую. Программы, написанные и оттранслированные в среде Windows должны исполняться в среде Linux, если в этой среде присутствует реализация JVM. Эта цель была реально достигнута. Программы на языке Java оказались переносимыми из одной среды в другую не только на уровне исходных тестов, но и на уровне кодов JVM, то есть в виде файлов с расширением .class.

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

Page 219: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

У JVM есть и противники. Их возражения сводятся, в основном, к критике быстродействия программ, написанных на языке Java. Безусловно, JVM работает медленнее, чем реальный процессор. Но если учесть, что скорости процессоров удваиваются, чуть ли не через два года, то это означает, что сегодняшняя JVM через два года будет работать быстрее, чем реальная сегодня.

Реализацией JVM в среде Windows является программа javaw.exe, поэтому запуск Java-программы заключается в запуске этой программы с передачей ей в качестве параметра имени класса, содержащего метод public static void main(String [ ] args).

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

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

Page 220: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

В языке вводится понятие пакета - package. Пакетом называется совокупность файлов, представляющих собой результат трансляции классов на языке Java и имеющих расширение .class. Каждый пакет располагается в директории, путь к которой начинается из директории, описанной параметром -classpath при запуске программы javaw.exe. Имя пакета представляет собой последовательность имен директорий, разделенных точками. В целях компактности хранения данных и большей безопасности при хранении на компьютере пользователя насколько пакетов могут храниться в архивном файле с расширением .jar. Архивы с расширением .jar доступны программам, управляющим исполнением программ на языке Java.

Например, пусть параметр classpath задан следующим образом:-classpath "С:\изегз\Владелец\Лоситег^з\Му

eBooks\Chapter04\BinaryTree\classes;С:\Borland\JBuilder 2005\jdkl. 4\jre\javaws\javaws.jar"

Приведенный параметр указывает, что программе будут доступны классы расположенные в директории С :\U БегзМЗладелецШосите^зМИуeBooks\Chapter04\BinaryTree\classes и файлеC:\Borland\JBuilder2005\jdkl.4\jre\javaws\javaws.jar”

Page 221: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Для того чтобы сделать в программе доступными классы собранные в пакет надо воспользоваться оператором import. Оператор import записывается перед ключевым словом class. Оператор import имеет две формы записи.

Первая форма записи:import <имя пакета>.*;

эта форма означает, что к программе подключается весь пакет.

Вторая форма записи:import <имя пакета>.<имя класса>;

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

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

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

Пакеты, входящие в состав языка, внешние пакеты.

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

Page 222: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Управление вводом-выводомДля демонстрации порядка применения пакетов

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

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

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

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

Page 223: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс File

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

Рассмотрим конструкторы этого класса:File (String filename); // в качестве параметра можно

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

File (String path, String filename); // в качестве // параметров уазывается полный путь к файлу / / и имя файла

File (File path, String filename); // в качестве // параметров указывается объект, соответствующий // директории, содержащей файл и имя файла

Класс File описывает много методов, позволяющих работать с файлами и директориями. Рассмотрим некоторые из них.String getName() // возвращает имя файла String getPath!) // возвещает полный путь файлу String getAbsolutePath() // возвращает полный путь файлу

// и имя файлаboolaen exists!) // доставляет true, если существует

// физический файл, соответсвующий созданному объекту // и false в противном случае

boolean isAbsoluteO // доставляет значение true, если // если файл является неперемещаемым и false в противном // случае. Большинство «обычных» файлов являются // перемещаемыми и для них этот метод вернет false

boolean isDirectory() // доставляет значение true, если// объект класса File связан с физическим файлом,// являющимся директорией

Page 224: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

boolean rename(String newName) // переименование файла // параметр задает новое имя файла // этот метод можно применять только файлам, не // являющимся директориями (isDirectoty() == false)

Boolean delete!) // удаляет физический файл, с которым // связан объект.// этот метод можно применять только файлам, не // являющимся директориями (isDirectoty() == false)

String (] list!) // возвращает список имен файлов,// содержащихся в директории, связанной с текущим // объектом класса File. Для него метод isDirectory() // доставляет значение true.

Рассмотрим проект ReadDirectory в директории Chapter06. Эта программа демонстрирует приемы работы с директориями. Она распечатывает содержимое директории, и, если очередной файл в рассматриваемой директории является тоже директорией, то рекурсивно вызывает метод public void print(File dir), что позволяет пройти на всю глубину директории и распечатать содержимое не только заданной директории, но всех входящих в нее поддиректорий.

import java.io.File;j к* <p>Title: </p>** <p>Description: Программа печати директории* и поддиректории </р>к* <p>Copyright: Copyright (с) 2003</р>к

* <p>Company: МИИТ</р>к* ©author Михайлюк А.В.* ©version 1.0 */

public class ReadDirectory {// конструкторpublic ReadDirectory!) {

// главная программаpublic static void main(String[] args) {

Page 225: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// создание объектаReadDirectory readdirectory - new ReadDirectory!); // вызвать метод печати, передавая ему начальную // директорию readdirectory.print

(new File("С:/mihailuk/]ava/graphics") ) ;

public void print (File file)(// метод применим только к директориям if (file.isDirectory()){

// получаем список файлов, входящих //в переданную директорию String[] list = file, list О;// цикл по всем входящим в массив // файламfor (int. i = 0; i < list, length; i + + ) {

// возьмем очередной эемент как файл File element - new File(file, list [i]); // печать данных об отдельном файле System.out.println(

(element.i sDirectory()? "Директория: ": "Файл данных ")+ element.getAbsolutePath());

// рекурсивный вызов метода для файлов, // являющихся директорами if (element.isDirectory())( print (element);

}

}

Запуск этой программы даст следующий результат:Файл данных C:\mihaiiulc\java\graphics\build.xml

Файл данных C:\mihailuk\java\graphics\manifest.mf Директория: С:\mihailuk\java\graphics\nbproject Файл данных C:\mihailuk\java\graphics\nbproject\build- impi.xml Файл данныхС:\mihailuk\java\graphics\nbproj ect\genfiles.properties Директория: C:\mihaiiuk\j ava\graphics\nbproject\private Файл данныхС :\mihailuk\java\graphics\nbproject\private\private.propertiesФайл данныхС :\mihailuk\java\graphics\nbproject\private\private.xml

Page 226: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Файл данныхС: \mihailuk\java\graphics\nbproject\project.properties Файл данныхС:\mihailuk\j ava\graphics\nbproj ect\project.xml Директория: C:\mihailuk\java\graphics\src Директория: C:\mihailuk\java\graphics\src\graphics Файл данныхС:\mihailuk\java\graphics\src\graphics\Main.java Директория: С:\mihailuk\java\graphics\test

Класс FilelnputStreamЭтот класс предназначен для чтения данных из файлов.

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

Конструкторы этого класса выглядят следующим образом:FilelnputStream (File file); // в качестве исходных

// данных для создания входного потока передается // объект класса File

FilelnputStream (String fileName); // в качестве // исходных данных передается имя файла

Объекты класса FilelnputStream обеспечивают побайтное считывание данных из файлов. Каждый байт приводится к целому числу. Числа, которые могут быть считаны из файла, находятся в диапазоне от 0 до 255. Попытка чтения данных после последнего байта данных в физическом файле дает результат -1, чго можно использовать в качестве надежного признака конца данных во входном потоке.

Рассмотрим основные методы класса FilelnputStream:int read() // возвращает следующий байт данных из

// входного потока, приведенный к целому числу int read (byte [] buffer) I I обеспечивает побайтное

// считывание данных из физического файла в // массив байтов. Возвращаемое значение указывает // количество прочитанных байтов

int available!) // доставляет количество байт в файле,I I доступных для чтения

void closed I I закрывает входной поток для чтения данных

Page 227: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс FileOutputStreamЭтот класс предназначен для организации вывода

данных в файл последовательности байтов.Конструкторы этого класса выглядят следующим

образом:FileOutputStream (File file) // создает выходной поток для

// вывода данных в указанный файл FileOutputStream (String fileName) // создает выходной

// поток данных для файла с указанным именемПоток готов для вывода в него данных

непосредственно после создания выходного потока. Если указанный файл не существовал в момент создания выходного потока, то создается новый файл. Если файл существовал, то старые данные пропадают и новые данные записываются в файл.

Рассмотрим основные методы работы с выходным потоком данных.void write (int data) // вывод одного байта данных в

// выходной поток. Целое число data должно иметь // значение в диапазоне от 0 до 255

void write (byte [] buffer) // вывод в выходной поток // массива байтов

void flash() // применение этого метода гарантирует, что // что все данные выведенные с помощью оператора // write будут физически записаны на носитель данных

void closed // закрывает поток вывода. Дальнейшие попытки // вывести данные в поток приведут к возникновению // исключительной ситуации IOException.

Класс PrintWriterЭтот класс предоставляет удобные возможности

редактирования для вывода различных типов данных в выходной поток.

Этот класс имеет конструктор:PrintWriter (FileOutputStream stream) // создает выходной

// поток для вывода печатных данных в выходной поток // данных

Page 228: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Основные методы для работы с выходным потоком данных для печати:void print (String string) // выводит в выходной поток

// указанную строку данныхvoid println (String string) // выводит в выходной поток

// указанную строку данных после чего выводит символы // перехода на новую стоку

В качестве примера использования пакета ввода- вывода рассмотрим проект FileSystem, расположенный в директории Chapter06. работа программы состоит из трех этапов.

На 1-м этапе идет работа с объектом класса File. Проверяется наличие на диске файла с именем TextFile.txt. Если такой файл уже есть, то он удаляется.

На 2-м этапе идет создание файла TextFile.txt. Для этого создается объект класса PrintWriter, и через него осуществляется построчный вывод сгенерированных строк, состоящих из случайно выбранных символов из исходной строки etalon.

На 3-м этапе выполняется считывание сформированного файла в массив символов. Для этого создается объект FileReader, предназначенный для чтения текстовых данных и с его помощью данные из файла TextFile.txt помещаюбся в массив символов, который потом выводится на печать

import java.io.File; import java.io.PrintWriter; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException;

! * ** <p>Title: Файловая система</р>** <p>Bescription: Демонстрация приемов работы* с файловой системой </р>

<p>Copyright: Copyright (с) 2009</р>

Page 229: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* <p>Company: МИКТ</р>■к

* gauthor Михайлюк А.В.* gversion 1,0Ч

public class FilesDemo {private static String etalon = "Сначала Степану "

+ "Трофимовичу повезло; за него ухватились и "+ "чего он и сам не знал); он подписался.";

public static void main(String[] args) {// описание файла как объекта языка Java File file = new FileCTextFile.txt");// провека, что указанный файл существует if ( file.exists()) {

System, out.println ("Удалить файл "+ f .i le. getAbsoiutePath () ) ;

// удаление найденного файла file.delete();

}try {

// "Оборачиваем" файл в объект предназначенный // для вывода символьных строк на печать PrintWriter output =

new PrintWriter(new FileOutputStream(file)); // цикл вывода данных в файл for (int i =0; i < 20; i++) {

// генерация отдельной строки данных String line ■= i + ") "

+ generateLine(20 t intRandom(10));System.out.println("Выводим: " + line);// вывод данных в файл output.println(line);

t ■ i// закрытие выходного файла ,,output.closeO;System.out.print In("Ввод данных");// "Оборачиваем" файл в объект для чтения данных FileReader in = new FileReader(file);// получаем длину файла long size = file.length();// создаем символьный буфер, чтобы разместить// весь файл в памятиchar И cbuf = new chart (int) size];// считываем файл в буфер in.read(cbuf);// закрываем файл in.close () ;System.out.print(cbuf);

)catch (IOException ex) {

Page 230: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

}

System.out.println("Ошибка ввода,вывода + ex.getMessage());

J * ** generateLine построение случайной строки* длинной length ■** Sparam length int* Sreturn String */

private static String generateLine(int length) {String result =for (int i = 0; i < length; i++) {result += etalon.charAt(intRandom(etalon.length())};

}return result;

)j * ★* intRandom построение случайного целого числа,* равномерно распределнного от 0 до diapazon - 1** Sparam diapazon int* Sreturn ObjectV

private static int intRandom(int diapazon) { return (int) (Math.randomO * diapazon);

}

Ниже приводится результат одного из запусков этой программы:Удалить файл С:\изегз\Владелец\0оситепбз\Му

eBooks\Chapter06\FileSystem\TextFile.txt Выводим: 0) еьг ттусьнзб л япуве Выводим: 1) еом"уттб аимсзстаивл Выводим: 2) ми нло ло п т" кнаан Выводим: 3) оноляин ледок оы ьа тн Выводим: 4) еепйс, м щнд боонняучл е Выводим: 5) оусвчтеаомолр нвпее ичэамраив Выводим: 6) е двбнсвлеитяксьтвп Выводим: 7) амелтедтрууОн ыинсчеи и Выводим: 8) илтллдиэяиол ттеуеоте чаряСтт Выводим: 9) м рс п дс ь,сиаст.дтао Выводим: 10) сПпвно и егТо лтв ыли Выводим: 11) нанавпян аи влооодг ьо Выводим: 12) бд п млна,ржислс млг сак Выводим: 13) д в к теоэй бзузул ибнапро ги

Page 231: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Выводим: 14) лтнжмльтк;сесао нхс эу енук Выводим: 15) иулксб тпбин рн оисане Выводим: 16) сзиту)а"лси н ыи еысик уи Выводим: 17) внявосодсмаг" нзтт,двх Выводим: 18) тнонжеи л о типо н Выводим: 19) П ябяпемп язмод а,лислм Ввод данных

еьг ттусьнзб л япуве1) еом"уттб аимсзстаивл2) ми нло ло п т" кнаан3) оноляин ледок оы ьа тн4) еепйс, м щнд боонняучл е5) оусвчтеаомолр нвпее ичэамраив6) е двбнсвлеитяксьтвп7) амелтедтрууОн ыинсчеи и8) илтллдиэяиол ттеуеоте чаряСтт9) м рс п дс ь,сиаст.дтао10) сПпвно и егТо лтв ыли11) нанавпян аи влооодг ьо12) бд п млна,ржислс млг сак13) д в к теоэй бзузул ибнапро ги14) лтнжмльтк;сесао нхс эу енук15) иулксб тпбин рн оисане16) сзиту)а"лси н ыи еысик уи17) внявосодсмаг" нзтт,двх18) тнонжеи л о типо н19) П ябяпемп язмод а,лислм

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

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

их применением в прикладных пользовательских

Page 232: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Ответ на этот вопрос следующий.Для создания пакета первым оператором в программе

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

Синтаксис оператора package следующий:package <имя1> [ . <имя2>] [ ■ <имяЗ>]

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

Предположим, что мы создаем проект TestLibrary, состоящий из описания уже знакомого нам класса Box, описывающего прямоугольные коробки с тремя измерениями - высотой, шириной и глубиной и двумя методами public double volumeQ, вычисляющего объем коробки и public String toStringO, возвращающего текстовое описание объекта класса Box. Предположим, что мы решили сделать описание этого класса библиотечным, то есть доступным из других проектов для использования. На этапе разработки надо выполнить два шага:

1. Разработать и отладить проект, содержащий классы, которые мы хотим сделать библиотечными.

2. Построить архивный файл, содержащий наш проект.

Page 233: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Разработка проекта, содержащего классы, включаемые в библиотеку

Проект разрабатывается, как обычно, но в начале класса мы пишем оператор

package mih_lib;Это означает, что тот модуль, который начинается этим

оператором, будет располагаться в директории mihlib, которая будет построена в текущем проекте в директории src. После компиляции проекта в директории classes будет построена аналогичная директория mih_lib, и в ней будет располагаться результат трансляции программного модуля с расширением class. Приведем текст описания класса Box, входящего в проект TestLibrary, расположенного в директории Chapter06: "package mih_lib;

public class Box (private double width, height, depth;

public Box(double width, double height, double depth) ( this.width = width; this.height = height; this.depth = depth;

public double volume(){return width * height * depth;

)public double getWidth (){

return width;

public double getHeight (){ return height;

public double getDepth (){ return depth;

public String toString ()( return "КороОочка " + width

+ " * " + height

Page 234: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

+ " * " + depth;}

}Построение архивного файла

Когда проект разработан и отлажен, исполните следующие шаги:

1. Выполните пункт меню File \ New... и в открывшемся окне мастера построения новых объектов укажите Archive \ Application \ ОК.

2. В открывшемся окне построения архива заполните поле Name. Остальные поля заполняйте в случае необходимости. Для продолжения работы нажмите кнопку Finish

Рис. 18. Окно мастера построения архива проекта3. После закрытия мастера построения архива проекта

исполните пункт меню Project \ Rebuild project. По этой команде проект будет перекомпилирован и построен архив TestLibrary.jar

Page 235: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Для того, чтобы воспользоваться классами, описанными в проекте LawyersRrj нам надо выполнить два шага:

1) подключить построенный архивный файл в качестве библиотеки к среде разработки JBuilder;

2) включить разработаннвй проект в новый проект для использования.

Подключение архивного файла в качестве библиотечного к среде разработки JBuilder

1. Выполните пункт меню Tools \ Configure \ Libraries... получите окно настройки библиотек, показанное на рис. 19.

Page 236: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

■’ • . '

Page 237: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 19. Окно настройки библиотек.2. Нажмите кнопку New... получите окно

добавления библиотеки. Заполните его в соответствии с расположением библиотеки, как показано на рис. 20.

Page 238: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.
Page 239: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 20. Окно мастера добавления новой библиотеки после заполнения поля Name, Location и выбора располжения добавляемого архивного файла.

3. Закройте оба окна, последовательно нажав два раза кнопки ОК.

Подключение разработанной библиотеки к новому проекту

В новом проекте воспользуемся оператором import, указав то имя пакета, которое было указано в операторе package подключаемой библиотеки. В качестве примера использования библиотеки рассмотрим проект usingLibrary, расположенный в директории Chapter06. Приведем текст проекта:irapoit mih_ 1 ib.Box;public class Demo {

public static void main(String[] argsi {Box box = new Box(10, 20, 30);System, out. print.in ' "Построен объект box = "

+ box+■ " объемом "+ b o x .v o l u m e ());

}Приведенный проект использует описание класса Box.

Проект благополучно транслируется и при запуске выдает следующий результат:Построен объект box = Коробочка 10.0 * 20.0 * 30.0 объемом

6000.0

Это говорит о том , что описание нашего класса Box стало доступно из проекта usingLibrary.

ВыводыПрименение пакетов помогает решать одну из

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

Во-первых, стандартные пакеты являются стандартным расширением языка программирования. К пользовательским

Page 240: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Page 241: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 7. Инкапсуляция - скрытие сложности для повторного использования

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

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

• public - свойство или метод доступен для использования объектами других классов.

• private - свойство или метод доступен для использования только в методах данного класса. Предполагается, что все свойства объектов не доступны непосредственно извне данного класса для чтения и изменения. Доступ к свойствам через методы позволяет контролировать целостность данных.

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

• отсутствие явной спецификации видимости. Это четвертый способ спецификации видимости. Он означает,

Page 242: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Рассмотрим в качестве примера проект Triangles, который расположен в директории Chapter07. Сам проект очень простой. Он описывает один класс Triangle, который содержит в качестве свойств длины трех сторон треугольника. Они описаны как private, поэтому у любого пользователя нет средств присвоить значения длинам сторон иначе как через конструктор. Но обратите внимание, что в состав конструктора входит вызов приватного метода test(), который проверяет непротиворечивость задания длин сторон. Если заданные три числа не могут образовывать стороны треугольника, то метод test() возбуждает исключительную ситуацию и конструктор не срабатывает. Такой подход гарантирует нам, что кто бы ни пользовался нашим классом Triangle в свих программах, он никогда не сможет построить объект класса Triangle со сторонами, не

Page 243: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

отвечающими правилу треугольника. При этом сам метод проверки скрыт от внешнего наблюдателя.public class Triangle {

private double a, b, c;public Triangle(double sa, double sb, double sc)

throws Exception { test(sa, sb, sc); a = s a ; b = sb; c = sc;

/*** test•k* @param sa double* @param sb double* (§param sc double */

private void test(double sa, double sb, double sc) throws Exception (

if (sa <■= 0 i I sb <= О II sc <= 0 II sa > sb + sc II sb > sa + sc II sc > sa + sb) (

throw new Exception(sa + ", " + sb T ", " + SC+ " не образуют треугольника");

public String toString(){ return "Треугольник (" + a

i ", " + b + ", " + c + ") " ;

public static void main(String[] aras) { for (int i = 0; i < 10; i++i{

try{Triangle t = new Triangle(Math.random()

, Math . random () , Math.randomO);System.out.println("Построен " + t);

)catch(Exception e){System.out.println("исключительная ситуация:

+ e.getMessage () ) ;

})

Page 244: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рассмотрим результат одного из запусков нашей программы. Из протокола работы программы легко видно, что объект класса Triangle создается только тогда, когда заданные три числа удовлетворяют правилу треугольника.исключительная с и т у а ц и я :0 . 1 73 157 23 572 78 737 8 ,

0 . 87 097 909 04 806 49 , 0 . 40012459829462244 не образуют т р еуг оль ни ка

Построен Треугольник (0 . 225 68 775544848074 , 0 . 48 545 075 89 765 137 , 0 .6098200674244142 )

исключительная с и т у а ц и я : 0 .2060 967 23 441 90 9 ,0 .889891 844 652 624 7 , 0 .046405565326182785 не образуют т реуго льн ика

исключительная с и т у а ц и я :0 . 2 28 486 32 976 49 735 2 ,0 .104962 506 122 401 66 , 0 . 36447528109411453 не образуют т р еу го льн и ка

исключительная с и т у а ц и я : 0 .1 7 87 5 31 41 0 74 4 58 94 ,0 .86154 248 621 119 16 , 0 . 592 083992526784 не образуют т реу го льн ика

Построен Треугольник (0 . 116 87 156069925131 , 0 . 58 18 890 28 325 349 7 , 0 . 6074561325041828 )

исключительная с и т у а ц и я : 0 . 165 62 81885695826 ,0 .21268 541 350 572 64 5 , 0 . 0016209657826203872 не образуют тр еуг оль ни ка

Построен Треугольник (0 . 845 09588068455 ,0 .7 311 083 80 764 637 7 , 0 .8560320537107211 )

исключительная с и т у а ц и я :0 . 1 71 482 60 281 19 802 4 ,0 .27211 976 852 947 29 5 , 0 . 843 97 35480705939 не образуют треуголь ни ка

исключительная с и т у а ц и я :0 . 3 27 746 29 542 24 759 ,0 .0 7776709747868 393 , 0 .6571058907617094 не образуют тр еуг оль ни ка

Page 245: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 8. Наследование - один из наиболее надежных способов повторного использования кода

Необходимость наследованияВспомним, что программирование - построение

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

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

• массой• габаритами• траекторией движения• скоростью и направлением движенияЭтих участников движения можно разделить на две

группы:• пешеходы• транспортные средстваПешеходы никаких дополнительных свойств от такой

классификации не получают, а вот все транспортные средства получают свойство наличие двигателя и количество колес.

В свою очередь транспортные средства делятся на:• Велосипеды• мотоциклы• автомобили

Page 246: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Автомобили делятся на:• легковые• грузовыеПодобная классификации может потребовать

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

Рис. 21. Иерархия объектов, участвующих в дорожном движении

Page 247: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Отметим некоторые свойства этих объектов, которые нам понадобятся:

• все объекты имеют общий объект, лежащий в основе классификации;

• на каждом уровне иерархии объекты получают новые свойства;

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

Правила построения новых классов на базе существующих

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

Дочерний класс строится на основании родительского. Для указания родительского класса в заголовке класса используется ключевое слово extends (расширяет), после которого пишется имя родительского класса. В языке Java реализован принцип: каждый класс имеет одного родителя. Рассмотрим проект Extentions, расположенный в директории Chaoter08. Он состоит из описания класса А, имеющего свойства private int х, у; конструктора, который задает значения свойствам и метода public int sumXY(), который возвращает сумму значений свойств х и у.

Page 248: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

В состав проекта входит также класс В, который является расширением класса А. В состав класса В входит свойство private int z, конструктор, который задает значения всех свойств и метод sumXYZ(), который возвращает сумму свойств а,Ь и z. Особенностью конструктора класса В является то, что он в качестве первого исполняемого оператора вызывает конструктор родительского класса. Такое положение дел является общим для всех конструкторов всех дочерних классов. Вызов конструктора родительского класса осуществляется с помощью специального оператораsuper(а, Ъ);

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

Класс Demo создает объект класса А и объект класса В и демонстрирует работу с объектами обоих классов.public class А {private int х, у;

public A (int a, int b) {X — а ;у = b;

}public int sumXY()(return x + у;

>Посмотрите на текст класса В:

• во-первых в строке заголовка класса есть фраза extends А, что говорит о том, что класс В расширяет класс А;

• во-вторых, в конструкторе в качестве первого исполняемого оператора записан оператор super (а, Ь); что

Page 249: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

говориг нам о том, что вызывается конструктор родительского класса;

• в-третьих, в методе sumXYZ() присутствует вызов метода sumXY(), что говорит нам о том, что в методах класса В доступны методы класса А;

• в-четвертых, в методе toStringQ есть конструкция super.toStringO, которая указывает на вызов метода toString(), принадлежащего родительскому классу.

public String toStr. ingOfreturn "Объект A (x=" + x + ”, y=" + у +

})public class В

extends A {private int z;

public В (int a, int b, int c) { super (a, b); z = c;

public int sumXYZ(){ return sumXY() + z;

1public String toString(){

return "Объект В на основе ("+■ super.toStringO + ”), z=" + z;

}!

Посмотрим на реализацию метода main() в классе Demo. В построении и использовании объекта класса А нет ничего необычного. Построение объекта класса В тоже ничего необычного мы не замечаем. А вот в использовании есть важный момент. Кроме метода b.sumXYZ(), принадлежащему классу В, вызывается метод sumXYQ, который в классе В не описан. Он унаследован классом В от класса А. Это есть проявление важнейшего свойства объектно-ориентированного программирования. Мы создали новый класс - класс В, наделили его новыми свойствами (private int z;) и новыми методами (public int sumXYZ()). При этом новый класс обладает свойствами и методами

Page 250: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

старого класса. То есть можно уверенно говорить о том, что дочерний класс расширяет возможности родительского класса.public class Demo (

public static void main(String[] args) (A a = new A (1,2);System.out.println("Построен " + a

+ " sumXY()=" + a .sumXY());В b = new В (2, 4, 8);System.out.println("Построен "

+ b + " sumXY ()=" + b. sumXY'()+ " sumXYZ () = " + b.sumXYZO);

}}

Приведенный ниже результат работы программы подтверждает правильность наших построений.Построен Объект А (х=1, у=2) sumXY()-3

Построен Объект В на основе (Объект А (х=2, у=4)), z=8 sumXY()-6 sumXYZ()=14

Рассмотрим еще один простой, но поучительный пример, демонстрирующий запуск конструкторов. Он представлен в проекте Constructors, расположенном в директории Chapter08. Проект состоит из 4-х классов: А, В, С и Demo. Класс В расширяет класс А, класс С расширяет класс В. Каждый из классов А, В и С состоит только из конструктора, который выводит на консоль сообщение о своем запуске. Ни конструктор класса В, ни конструктор класса С не содержит явного вызова конструктора своего родительского класса. Класс Demo содержит только оператор создания одного объекта класса С, но консольный вывод ясно содержит информацию о последовательном вызове конструкторов классов А, В и С.class А{

public А (){System.out.println("Конструктор класса А");

class В extends А{ public В() {

Page 251: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System, out. .println ( "Конструктор класса В");

)class C extends B{ pubi ic C()t

System.out.println{"Конструктор класса С");

public class Demo {public static void main(String[] args) (

C c = new C () ;}

)

Ниже приведены результаты работы программы:Конструктор класса А

Конструктор класса В Конструктор класса С

Класс Object - прародитель всех классов

В предыдущем разделе было сказано, что при описании класса можно указать extends и в нем указать имя родительского класса, а есть ли родитель у классов, которые мы описывали до сих пор? Ответ - да, есть. Таким классом является класс Object из пакета java.Iang. В языке определено, что этот класс является родителем или прародителем всех классов языка Java. Рассмотрим этот класс.

Метод Описание

public Object clone() Создает точную копию объекта

public boolean equals(Object object)

Воозвращает true, если вызывающий объект идентичен объекту object

Page 252: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public void finalize() throws Throwable

Метод, вызываемый при уничтожении объекта

public final Class getClass()

Создает объект класса Class, описывающий вызывающий объект

public int hashCode() Возвращает хэш-код объекта

public final void notify() Возобновляетисполнение потока, ожидающего вызывающий объект

public final void notifyAll()

Возобновляетвыполнение всех потоков, ожидающих вызывающий объект

public String toString() Возвращает строку, описывающую вызывающий объект

public final void wait() throws InterrupedException

Переводит вызывающий поток в состояние ожидания

public final void wait(long inillisecs) throws InterrupedException

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

public final void wait(long millisecs, int nanosecs) throws InterrupedException

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

К сожалению, пока мы не разъяснили назначение спецификатора final и не рассматривали понятие «потока».

Page 253: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Перекрытие методов. Динамический вызов методов

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

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

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

protected double a; // сторона квадрата

public Quadrate (double side)! // конструктор a = side;

}// вычисление площади фигурыpublic double square ( i (

return a * a;

public String toString()(

Page 254: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

проекта Assignementl, расположенного в директории Chapter08. В этом проекте определены три класса А, В и С, причем В расширяет А, С расширяет В. В классе Demo описана одна переменная класса А. Ей по очереди присваиваются значения объектов класса А, потто В, потом С, и каждый раз выводится на печать имя класса объекта, присвоенного переменной а. Классы разные, но присваивания не вызывают проблем, поскольку все они наследуются от класса А.class А{}class В extends А(}class С extends В{}public class Demo {

public static void main(String[] args) {A a = new A () ;System.out.println("Имя класса: "

+ a . geLClass() .getName());a = new BO;System.out.println!"Имя класса: "

+ a .getClass().getName()i;a = new CO;System.out.println("Имя класса: "

+ a.getCLass().getNameО ) ;}

lПосмотрите на результат работы программы:

Имя класса: А Имя класса: В Имя класса: С

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

Page 255: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Перекрытие методов. Динамический вызов методов

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

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

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

protected double a; // сторона квадратаpublic Quadrate (double side){ // конструктор

a = side;}// вычисление площади фигурыpublic double square()(

return a * a;)public String toString(){

Page 256: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

return "Квадрат (" f a * "x" -t- a + I

}

В этом случае можно определить прямоугольник, как расширение понятия квадрата и дать ему следующее описание:

class Rectangle extends Quadrate(private double b;// конструкторpublic Rectangle(double width, double height){

super (width); b = height;

)// площадь прямоугольникаpublic double square(){

return a * b;}public String toString(){

return "Прямоугольник (" + a * "x" + b + ")";

i

Обратите внимание, что метод public double square () в наследуемом классе перекрывает метод с таким же именем и таким же списком параметров. В приведенной ниже программе переменная t по очереди ссылается на объекты класса Quadrate и Recrangle. При этом оба раза вызывается метод t.square().

public class Demo {public static void main(String[] args) {

Quadrate t = new Quadrate(5);System.out.println("Создан объект " + t

+ " с площадью " + t.squareO); t = new Rectangle (5, 6);System.out.println("Создан объект " + t

+ ” с площадью " + t.squareO);}

)

Page 257: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Приведенная программа записана в проект Figures, находящийся в директории Capter08. Результаты ее запуска приведены ниже. Они ясно показывают, что метод public double square() класса Rectangle перекрывает аналогичный метод родительского класса Square.Создан объект Квадрат (5.0x5.0) с площадью 25.0

Создан объект Прямоугольник (5.0x6.0) с площадью 30.0

Рассмотрим еще один пример вызова перкрывающих друг друга методов.

Построим три класса А, В и С. А является родительским для В и С. Все три класса определяют метод public void method(), который выводит на печать свое собственное имя. Заполним массив из 10 элементов объектами всех трех типов, и в цикле вызовем их метод methodQ.class А)public void method!){

System.out.printIn{"A.method()");

}class В extends A{public void method!){

System.out.print in("B.method()");}

)class C extends A{public void method!){

System.out.println("C.method()") ;)

)public class Demo i

public static void main(String[] argsj { A [J a = new A [10]; for (int i = 0; i < a.length; i++){ switch ( intRanaom (. 3) ) {case 0: a [i1 = new A(); break;case It а [П = new B(); break;case 2: a [il = new CD;

}

Page 258: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

)for ( i n t i = 0; i < a . l e n g t h ; i + + ) {

a[i].method();

>p r i v a t e s t a t i c i n t i n tRandom ( i n t d i a p a s i o n ) {

r e t u r n ( i n t ) ( M a t h . r a n d o m () * d i a p a s i o n ) ;)

В .method()C.method() B.method() В.method() A.method()A. method()B . method()A. method()B . method() A.method()

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

Page 259: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 9. Абстракция - один из способов отложить решение задачи на более поздние этапы

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

Рассмотрим, как современный язык программирование поддерживает приемы абстрактного мышления

Абстрактные классы и абстрактные методы , как прием переноса решения о поведении объекта на более поздние стадии разработки.

Предположим, что мы разрабатываем систему объектов, описывающих всевозможные фигуры на плоскости. Скорее всего, мы создадим класс, описывающий все плоские фигуры, предположим, что имя этого класса будет Figure. Во множество объектов, относящихся к этому классу, будут входить и окружности, и треугольники, и четырехугольники, и все остальные плоские фигуры, с которыми мы будем работать. Предположим, что мы ограничиваем наше рассмотрение только такими фигурами, которые имеют площадь. В этом случае мы с уверенностью можем сказать, что объект класса Fugure обладает методом public double square(), который возвращает площадь фигуры. Но на этом все и заканчивается. Мы не можем написать тело метода потому, что для разных фигур способ вычисления площади различен, и на этапе проектирования класса Figure его логика не определена. Когда мы дойдем до конкретных

Page 260: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

фигур, то вопрос этот станет ясен. Для круга это Math.Pl * г * г. для треугольника это знаменитая формула Герона, для прямоугольника - это произведение ширины на высоту, для особо сложных фигур это могут быть методы приближенного вычисления площади. Но это будет потом, когда мы будем описывать классы Cicle (круг), Triangle, Rectangle и другие. Поскольку понятие площади фигуры принадлежит всем классам, входящим в понятие «фигура», то мы описываем метод squareQ следующим образом:p u b l i c a b s t r a c t d o u b l e s q u a r e d ;

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

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

Синтаксическим признаком абстрактного класса является спецификация abstract перед ключевым словом class.

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

Рассмотрим пример построения иерархии классов с использованием абстрактного класса и абстрактного метода. Для этого рассмотрим проект Figures, находящийся в директории Chapter08.

Page 261: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 22. Структура порождения классов от абстрактного класса Figure.

Текст программы приведен в проекте Figures, который находится в директории Chapter09.a b s t r a c t c l a s s F i g u r e {

/ / описание аб с т р а к т н о го метода p u b J i c a b s t r a c t d o u b l e s q u a r e ! ) ;

c l a s s C i r c l e e x t e n d s F i g u r e ;/ / радиус круга p r i v a t e d o u b l e r ;

/ / конструкторp jub l i c C i r c l e (dou b l e r a d i u s ) !

i = r a d i u s ;}

/ / описание конкретного метода / / вычисления площади круга p u b l i c do u b l e s q u a r e ; ) l

r e t u r n M a t h .P I * r * r ;)

/ / описание квадр ат а c l a s s Q u a d ra t e e x t e n d s F ig u r e !

/ / сторона к ва др ат а p r o t e c t e d d o u b l e a ;

/ / конструктор

Page 262: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public Quadrate (double size){ a = size;

}// описание конкретного метода // вычисление площади квадрата public double square(){

return a * a;

// описание прямоугольника class Rectangle extends Quadrate!

// вторая сторона прямоугольника private double b;

// конструкторpublic Rectangle(double height, double width)

// вызов конструктора родительского класса super (height); b = width;

}// вычисление площади прямоугольника public double square(){

return a * b;

}public class Demo (

public static void main(String[] args) ( Figure t -- new Circle (1);System.out.print In("Создан объект "

+ t.getClass().getName()+ " площадь " i - t. square () ) ;

t = new Quadrate(5);System.out.println("Создан объект "

+ t .getClass().getName()+ " площадь ” + t.squareO);

t = new Rectangle (5, 6);System.out.println("Создан объект "

i t.getClass().getName()+ " площадь " + t .square());

))

Результат работы программы:Создан объект Circle площадь 3.141592653589793 Создан объект Quadrate площадь 25.0

Page 263: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Создан объект Rectangle плошадь 30.0Естественно может возникнуть вопрос: нужны ли

абстрактные методы? Представим себе, что мы хотим написать точно такую же программу, но без использования абстрактного метода, тогда класс Figure не будет обладать методом square() и вызов t.square() будет невозможен.

Интерфейсы, как более высокая ступень абстракции.

В изучаемом нами языке каждый класс имеет в точности одного непосредственного предка: либо этим предком будет класс Object, либо класс, указанный во фразе extends оператора class. Это означает, что новый класс может наследовать свойства и методы только одного класса, и, разумеется, всех его прямых предков. Наследовать свойства и методы двух разных классов невозможно. Это довольно серьезное ограничение. Очень часто встречается ситуация давно описанная Николаем Васильевичем Гоголем в комедии «Женитьба». Невеста Агафья Тихоновна, рассуждая о свойствах женихов, мечтает: «Если бы губы Никанора Ивановича да приставить к носу Ивана Кузьмина, да взять сколько-нибудь развязности, какая у Балтазара Балтазарыча, да, пожалуй, прибавить к этому еще дородности Ивана Павловича - я бы тогда тотчас же решилась». Если подумать об этом желании невесты, чтобы ее жених сочетал в себе лучшие свойства всех претендентов на ее руку, то это не такое уж и смешное желание. В жизни ребенок наследует свойства (не всегда саамы лучшие) от двух родителей - отца и матери. В языке Java - только от одного, того, который он расширяет.

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

Page 264: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Поэтому авторы языка Java приняли простое и правильное решение: каждый класс может иметь только одногонепосредственного предка.

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

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

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

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

Page 265: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

обязательно. Кроме заголовков методов в интерфейсахможно описывать константы. Описания констант в Jave похоже на описание обычных свойств, но есть две особенности:

• в спецификации свойств должно присутствовать ключевое слово final;

• константа должна иметь инициализирующеевыражение

Описание интерфейса имеет следующий вид:interface <имя итерфейса> {

сописание константы 1>;<описание константы 2>;■сописание константы R>;«заголовок метода 1>;«заголовок метода 2>;«заголовок метода N>;

}

Например, описание констант в некотором интерфейсе может выглядеть следующим образом:final int RED_MASK = OxFFOOOO; final int GREEN_MASK = OxOOFFOO; final int BLUE_MASK = OxOOOOFF;

Описание методов интерфейса может выглядеть следующим образом: double square();double average (double ('] array);

Выше приведено описание двух методов: метода square(), вызываемого без параметров и возвращающего значение типа double, и метода average(), получающего в качестве параметра массив чисел с двойной точностью и возвращающего значение в виде числа с двойной точностью.

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

Page 266: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Например:interface В{

class A implements В{

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

класс может реализовывать любое количество интерфейсов. Записывается это очень просто. После ключевого слова implrments через запятую перечисляются все интерфейсы, реализуемые в описываемом классе. Фактически это положение решает проблему множественного наследования. Можем реализовать несколько интерфейсов, фактически наделив описываемый класс поведением нескольких разных классов. Например:class A implements В, С, D {

}где В, С и D - имена интерфейсов.Второе правило использования интерфейсов требует,

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

Для того, чтобы можно было вызывать методы, описанные в интерфейсе можно определить перемененную, имеющую тип этого интерфейса и присвоить ей значение объекта, реализующего этот интерфейс. Например, если мы имеем описания, приведенные выше, то мы можем написать:А а = new АО; // создали объект класса А, реализующий

// интерфейс ВВ Ь = а; // переменная b указывает на объект,

// реализующий интерфейс ВРассмотрим проект Figures2, находящийся в

директории Chapter09. На первый взгляд он мало чем отличается от проекта Figures, рассмотренного выше, но на

Page 267: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

самом деле отличия существенные. Классы Circle и Quadrrate не являются расширением абстрактного класса Figure, они реализуют интерфейс Figure, предназначенный для описания поведения двумерных фигур на плоскости.// описываем интерфейс, определяющий поведение // двумерных фигур, расположенных на плоскости interface Figure{

// методов может быть сколько угодно,// но мы рассмотрим интерфейс, описывающий // только вычисление площади фигуры double square ();

}// опишем отдельные фигуры// описание круга class Circle implements Figure{

private double r;// конструкторpublic Circle (double radius){

r = radius;)// реализация метода из интерфейса Figure public double square!){

return Math.PI * r * r;}

// описание квадрата class Quadrate implements Figure { protected double a;public Quadrate(double size){

a = size;}// реализация метода из интерфейса Figure public double square)){

return a * a;)

// описание прямоугольника,// как расширения квадратаclass Rectangle extends Quadrate)

Page 268: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

private double b;public Rectangle(double width, double height!)

super(width); b = height;

public double square!)( return a * b;

)

public class Demo {public static void main(Stringf] args) (

Figure t = new Circled);System.out.println("Создан объект "

+ t.getClass().getName()+ " плошадь " + t.squareO);

t = new Quadrated);System.out.println("Создан объект "

+ t .getClass().getName()+ " площадь " + t.squareO);

t = new Rectangle(5, 6);System.out.println("Создан объект "

+ t .getClass().getName()+ " площадь " + t.squareO);

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

Создан объект Quadrate площадь 25.0 Создан объект Rectangle площадь 30.0

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

В качестве примера рассмотрим два взаимосвязанных проекта SortPackage и StaffList.

Проект SortPackage описывает пакет mihailuk.utils.sort. В этом пакете должны храниться всевозможные методы

Page 269: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

сортировки массивов. Для краткости описан один метод сортировки - метод пузырька. Мы с вами уже знакомы с различными методами сортировки массивов, и когда вы разберетесь, как устроен проект, то без труда сможете самостоятельно расширить его другими, более совершенными методами сортировки.

Для того, чтобы пакет, выполняющий сортировки массивов, был универсальным, создадим интерфейс Comparable, описывающий одно свойство элементов массива - должен существовать способ сравнения элементов массива. Конкретная природа элементов массива нам безразлична. Важно только, чтобы метод сортировки мог вызывать один единственный метод public int compare(Object object), который выполнит сравнение двух объектов и выдаст целое число, говорящее о результате сравнения двух объектов. Если результат будет больше нуля, то эго означает, что текущий объект больше того, с которым он сравнивается, если меньше нуля, то текущий объект меньше того, с которым сравнивается, и ноль в случае равенства.

Рассмотрим проект SortPackage, хранящийся в директории Chapter09:package mihailuk.utils.sort;

/•** <p>Title: Sortabble</p>** <p>Description: интерфейс описывает необходимый* для сртировки метол сравнения двух объектов</р> +* <p>Copyrig'nt: Copyright (с) 2009</р>

* <p>Company: </р>•** Sauthor not attributable* Aversion 1.0 */

public interface Comparable {

// метод выполняет сравнение двух объектов:// текущего, которому он принадлежит и // переданному ему в качестве параметра // если текущий объект меньше страниваемого,

Page 270: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// то возвращается значение меньше нуля,// если текущий объект больше сравниваемого,// то возвращается значение больше нуля и,// если они равны, то ноль, int compare (Object object);

)package mihailuk.utils.sort;

/*** <p>Title: Сортирующий класе</р>

* <p>Description: объекты этого класса умеют* сортировать массивы объектов, реализующих* интерфейс СотрагаЫе</р>•** <p>Copyright: Copyright (с) 2009</р>

* <p>Company: М И Т </р>-*4 @author Mihailuk A.V.■* @version 1.0 V

public class Sorter {

public static void bulbSort (Object [] array){ for (int i = array.length - 1; i > 0; i— ){

for (int j = 0; j < i; jt-+) {int result =■• ((Comparable)(arrayfj ]) )

.compare(array[j + 1]); if (result > 0){

Object t = array [j]; arraytj] = array [j + 1]; array [j + 1] = t;

}}

Централным моментом проекта является строкаint result - ((Comparable)(array[j]))

. compare (ar ray [ j 1] ) ;которая вызывает метод сравнения двух элементов

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

Page 271: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

библиотека. Проект собран в архив SortPackage.jar, который подключен к проекту StaffList.

Проект StaffList описывае два класса:• класс Staff, реализующий интерфейс Comparable• класс Main, объединяющий в себе работу с

классом Staff и использование библиотеки SortPackage./*** <p>Title: Staff</p>к

* <p>Description: Описызает класс Staff,* реализующий интерфейс Comparable из* пакета mihailuk.utiIs.sort</p>к

* <p>Copyriqht: Copyright (c.) 2009</p>+* <p>Company: </p>** ©author not attributable* ©version 1.0

import mihaiLuk.utils.sort.Comparable;

public class Staff implements Comparable { private String name; private String position; private int salary;

// конструкторpublic Staff(String name, String position,

int salary) { this.name = name; this.position = position; this.salary = salary;

// метод сравнения объектов,// реализующий интерфейс Comparable public int compare(Object o) (

int result =name.compareToIgnoreCase(((Staff)o).name);

return result;}public String toString (){

return "Имя " -*■ namea " Должность " + position

Page 272: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

+ " Оклад " + salary;

/*** createStaff фабрика, производящая объекты* класса Staff

* Sreturn Staff */

public static Staff createStaff() {return new Staff(generateName()

, generatePosrLion(), generateSala г у()i;

f

f * *

* generateSa1 ary генератор окладов сотрудников** 0return int4

private static int generateSalary() (return intRandom(20) * 1000 + 2000;

}/**’ intRandom генератор случайных чисел* в дипазоне от 0 до i - 1 ■** 0parara i int - диапазон случайных целых чисел* (sreturn int */

private static int intRandom(int i) { return lint)(Math.random(i * i);

/*** generatePosition генератор должностей* сотрудников** Sreturn String */

private static String generatePosition() { String [] positionList = ("Manager"

, "Salesman", "Programmist", "Anaiitic”); return positionList

[intRandom(positionList.length)] ;

/*'*generateName генератор имен сотрудников

Page 273: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Sreturn String */

private static String generateName!) { int length = intRandom (15);String result = ""

+ (char) ('A' + intRandom (' Z.' - 'A' +- 1) ) for (int i = 0; i < length; i + + ){

result += (char)('a '+ intRandom('z' - 'a' + 1

return result;

/*** <p>Title: </p>Jr

* <p>Description: </p>•** <p>Copyright: Copyright (c) 2009</p>* <p>Company: </p>* Sauthor not attributable* 0version 1,0 */

import mihailuk.utils.sort.Sorter; public class Main {

private Staff П staffs;public Main(int size) {

staffs = new Staff[size];)public static void main(String[] args) {

Main main = new Main(10); main. f iUStaf f s () ; main.print();Sorter.bulbSort(main.s taffs); main.print();

)/*** fillStaffs*/

private void fillStaffs () {for (int i = 0; i < staffs.length; i++) {

staffs [i] = Staff.createStaff ();

Page 274: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

privat.e void print () {System.out.println("Список сотрудников"); for (int i = 1; i <= staffs.length; i++){

System.out.println(i + ”) " + staffs[i - 1]);

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

1) Имя Bslejtns Должность Programmist Оклад 100002) Имя Qmtb Должность Salesman Оклад 120003) Имя Olxqhombbtdlwx Должность Analitic Оклад 80004) Имя Kpudepeyi Должность Programmist Оклад 110005) Имя Giluqacrwg Должность Manager Оклад 110006) Имя Hknbbnlhmquzvgc Должность Salesman Оклад 30007) Имя Tgysgsabg Должность Programmist Оклад 70008) Имя Yscwvjufxab Должность Salesman Оклад 140009) Имя Kfuycibucikp Должность Analitic Оклад 12000 10} Имя Tcj Должность Programmist Оклад 21000 Список сотрудников1) Имя Bslejtns Должность Programmist Оклад 100002) Имя Giluqacrwg Должность Manager Оклад 110003) Имя Hknbbnlhmquzvgc Должность Salesman Оклад 30004) Имя Kfuycibucikp Должность Analitic Оклад 120005) Имя Kpudepeyi Должность Programmist Оклад 110006) Имя Olxqhombbtdlwx Должность Analitic Оклад 80007) Имя Qmtb Должность Salesman Оклад 120008) Имя Tcj Должность Programmist Оклад 210009) Имя Tgysgsabg Должность Programmist Оклад 700010) Имя Yscwvjufxab Должность Salesman Оклад 14000

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

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

Page 275: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Page 276: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 10. Полиморфизм - один из способов борьбы со сложностью

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

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

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

Рассмотрим простой операторх = а + Ь;

Мало кто из нас усомнится в том, что переменной х присваивается значение суммы переменных а и Ь. Однако внимательный читатель насторожится и спросит, а каковы типы переменных х, а и Ь? Если все три переменные имеют числовой тип, то числовые значения переменных а и b будут просуммированы и переменной х будет присвоено значение суммы. Если переменные а и b - строковые переменные, то знак + будет указывать на конкатенацию строк. Однако с числовыми значениями все не так однозначно, если все переменные имеют тип int, то для вычисления суммы будет использоваться целочисленная арифметика. Если же переменные описаны как числа с плавающей точкой - float или double, то для вычисления суммы будет использоваться арифметика с плавающей точкой. Если типы переменных разные, например, х и а - числа с плавающей точкой, а b - целая переменная, то для вычисления суммы будет сначала

Page 277: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Перегруженность методов. Понятие сигнатуры метода.

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

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

Page 278: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

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

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

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

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

Рассмотрим простой пример применения перегруженных методов, приведенный в проекте Overload, находящийся в директории Chapter 10. Проект состоит из описания класса Overload, содержащего перегруженный метод public void printParams(...), и класса Demo, использующего объект класса Overload и определенный в нем метод printParams. Текст обоих классов приведен ниже. /*** <p>Title: </р>* <p>Description: </р>* <p>Copyright: Copyright (с) 2009</р>** <p>Company: </р> ** 0author not attributable

Page 279: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

* Aversion 1.0* /

public class Overload { public Overload!) {)public void printParams()(

System.out.println("Без параметров");

public void printParams(int i){System.out.println("int i = " + i);

}public void printParams(byte b){

System.out.println("byte b - " + b);}public void printParams(long 1){

System.out.println("long 1 - " + 1);}public void printParams(int x, int y)(

System.out.println("int x = " + к

/**'* <p>Title: </p>* <p>Description: </p>*■* <p>Copyright: Copyright (c) 2009</p>■** <p>Company: </p>•k

* ^author not attributable* @vers ion 1.0 */

public class Demo {public static void main(String[] args) (

Overload о = new Overload!);// вызывается метод без параметров о .printParams(); int а - 22;// вызывается метод с целым параметром о .printParams(а); byte b - 22;// вызывается метод с параметром byte о . pi int.Params (b) ;

Page 280: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// вызывается метод с целым параметром // поскольку при вычислении значения // выражения Ь + 1 байт обобщается до целого о .printParams(b + 1); long с = 22;// вызывается метод с параметром long о.printParams(с);// вызывается метод с двумя целыми параметрами // поскольку параметр Ь автоматически приводится // к целому типу о.pr.intFarams (а, Ъ) ;

}Запуск программы дал следующий результат:

Без параметров int i = 22 byte b = 22 int i = 23 long 1 = 22int x = 22, int у = 22

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

Удобны ли перегруженные методы? Конечно, да, удобны. Можно ли было обойтись без них? Да, можно, достаточно придумывать методам похожие, но различные имена. Но есть один случай, где без перегруженных методов не обойтись. Только тут мы встречаемся не с перегруженными методами, а с перегруженными конструкторами. Конструкторы и простые методы очень похожи друг на друга. Вспомним, что имя конструктора всегда совпадает с именем класса. Если бы не было перегруженных конструкторов, то у нас была бы возможность описать только один конструктор для класса. Эго было бы очень серьезным ограничением наших возможностей писать удобные программы. Фактически мы уже неоднократно пользовались этой возможностью языка, описывая в разных проектах классы, имеющие более одного конструктора. Просто, мы не акцентировали на этом внимания. Заметьте, мы опять же пользовались свойством

Page 281: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Page 282: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Глава 11. Многопоточность - способ отражения параллельно развивающихся процессов реального мира

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

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

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

Понятие потока. Способы описания и запуска потоков.

В языке вводится понятие «потока». По-английски этот термин звучит как thread. В буквальном переводе thread

Page 283: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

означает «нить», но в русском языке утвердился перевод «поток». К сожалению, слово «поток» используется и для описании ввода/вывода. Там оно используется для перевода термина Stream. Но с этим неудобством приходится мириться, различая по контексту, о каком потоке идет речь.

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

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

Класс Object имеет методы, связанные с управлением потоками.

Рассмотрим основные конструкторы, методы и свойства класса Thread.

Определение j Описание

Конструкторы

public Thread() Создает новый поток со стандартными параметрами

publicThread(Runnable target)

Создает новый поток с передачей ему объекта, реализующего интерфейс Runnable

Page 284: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

publicThread(String name)

Создает стандартный поток с указанным именем

publicThread(Runnable target, String name)

Создает новый поток с передачей ему объекта, реализующего интерфейс Runnable, потоку присваивается указанное имя

Управление потокомp u b l i c v o i d start {)?

Запускает поток после его создания

public void run();

Содержит код, который должен исполняться в отдельном потоке

public static ThreadcurrentThread();

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

public static void sleep(long mills) throws InterruptedException;

Приостанавливает текущий поток на указанное число миллисекунд

public final boolean isAliveO;

Возвращает true, если поток начал исполняться и еще не закончился. То есть start() уже исполнен, а шп() еще не завершился

Управление приоритетамиp u b l i c final s t a t i c int M I N P R I O R I T Y « 1; Минимальный

приоритет пока

public final j Нормальный приоритет

Page 285: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

static int NORM_PRIORITY = 5;

потока

public final static int MAX_PRIORITY = 10;

Максимальный приоритет потока

public final void setPriority(int newPriority)

Установить указанный приоритет потока

public final int getPriority()

Дать приоритет потока

Прочие методы управления потоками

public final void setName(String name)

Установить имя потока

public final String getName()

Дать имя потока

public static int activeCount()

Возвращает количество активных потоков

public final void join(long mills) throwsInterruptedException

1

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

public final void join() throws InterruptedException

Приостанавливает текущий поток до тех пор, пока не завершится поток, к которому применен этот метод.

Методы класса Object, используемые в управлении

Page 286: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

потокамиpublic final

native void wait(long timeout) throws InterruptedException;

Останавливает текущий процесс на указанное число миллисекунд или до тех пор, пока другой поток не выдаст на текущий объект notify() или notifyAll()

p u b l i c final v o i d n o t i f y ();Будит один из потоков,

ожидающих тот монитор, в котором выдан метод wait()

public final native void notifyAll();

Будит все потоки, ожидающих тот монитор, в котором выдан метод wait()

Поток главной программыДля запуска главной программы исполнительная

система языка Java создает специальный поток, в котором эта программа автоматически запускается. Для изучения свойств потока главной программы напишем программу, приведенную в проекте MainThread, расположенном в директории Chapter 11. Вот ее текст:public class' Main {

public static void main(String!] args) (// получение текущего потока Thread t = Thread.currentThread();System.out.println("Имя потока " + t.getNameO);System.out.println("Приоритет " + t.getPriorityО !; t .setName("Главный поток"); t.setPriority(7);System.out.println("Имя потока " + t.getNameO);System.out.println("Приоритет " + t .getPriority());

Рассмотрим результаты работы этой программы:Имя потока main Приоритет 5Имя потока Главный поток Приоритет 7

Page 287: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Наглядно видно, что главная программа запускается под управлением потока с именем main и приоритетом равным 5. Свойства потока, управляющего главной программой, доступны не только для чтения, но и для изменения.

Создание потока путем расширения класса ThreadПервый рекомендованный авторами языка способ

создания потоков в прикладных программах на языке Java - описание класса, расширяющего класс Thread. При этом необходимо только переопределить метод public void run().

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

Рассмотрим проект ExtendThread, находящийся в директории Chapter! 1./*

Создание нового потока путем расширения класса Thread*/class NewThread extends Thread {

public NewThread Ofsuper ("Дочерний поток");System.out.println

("Дочерний поток создан"); start (); // Запуск потока

}public void run (){

try{for (int i = 0; i < 5; i++)(

Syst era. out. println("Дочерний поток 1 == " + i);

Thread.sleep (500);}

i catch (InterruptedException e)( System.out.println

("Interrupted Exception "+ " e дочернем потоке: " + e);

}System.cut.println

("Дочерний поток завершается");

Page 288: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

}}public class ExtendThread {

static public void main(String args[]) { new NewThread(); try {

for (int i = 0; i < 5; i++){System.out.println("Главный поток i == " + i);Thread.sleep (1000);

)) catch (InterruptedException e)(

System.out.println("Interrupted Exception "+ " в главном потоке");

}System.out.println ("завершение программы");

}Ниже приведен протокол работы этой программы

Дочерний поток создан Главный поток i == 0 Дочерний поток i == 0Дочерний поток i == 1Главный поток i == 1 Дочерний поток i 2Дочерний поток i == 3Главный поток i == 2 Дочерний поток i == 4 Дочерний поток завершается Главный поток 1 == 3 Главный поток i == 4 завершение программы

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

Page 289: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Создание потока путем реализации интерфейса Runnable

Более гибкий метод создания потоков - реализация интерфейса Runnable. Определение интерфейса приведено ниже:package java.lang; public interface Runnable {

public void run();}

Вы видите, что интерфейс требует только описания метода public void шп(). Правила создания потоков:

• определите новый класс и укажите в нем, что он реализует интерфейс Runnable;

• определите в описываемом классе свойство private Thread t;

• опишите в качестве одного из методов метод public void run(), поместив в него те действия, которые вам надо исполнить в отдельном потоке;

• в конструкторе описываемого класса укажите следующие шаги

о t = new Thread (this); по этому операторусоздастся объект класса Thread. В качестве параметра конструктору Thread предается текущий объект;

о t.start(); по этому оператору происходитзапуск потока.

В качестве примера создания потока в прикладной программе путем реализации интерфейса Runnable рассмотрим проект ImplementsRunnable, находящийся в директории Chapter 11./*Создание нового потока путем реализации интерфейса Runnable

*/class NewThread implements Runnable { private Thread t;

public NewThread (){

Page 290: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

t = new Thread (this, "Дочерний поток") System.out.println

("Дочерний поток создан"); t.start (); // Запуск потока

public void run (){ try{

for (int i = 0; i < 5; i++){System.out.println

("Дочерний поток i == " + i); Thread.sleep (500);

}} catch (InterruptedException e){

System.out.println("Interrupted Exception "+ " в дочернем потоке: " + e) ;

)System.out.println

("Дочерний лоток завершается");}

}public class ImplementsRunnable {

public static void main(String[] args) ( new NewThread!); try{

for (int i = 0; i < 5; i++)(System;out.println("Главный поток i == " + i);Thread.sleep (1000);

}) catch (InterruptedException e){

System.out.println("Interrupted Exception "+ " в главном потоке");

)System.out.println ("завершение программы")

Рассмотрим протокол работы программы:Дочерний поток создан

Главный поток i =» 0 Дочерний поток i == 0 Дочерний поток i == 1 Главный поток i == 1 Дочерний поток i == 2 Дочерний поток i == 3 Главный поток i == 2

Page 291: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Дочерний поток i == 4 Дочерний поток завершается Главный поток i == 3 Главный поток i == 4 завершение программы

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

Состояния потоковПоток последовательно создается, запускается,

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

Название Перевод Пояснения

NEW Новый Послесоздания до исполнения метода start()

RUNNABLE Работоспособен

Можетисполняться или быть готовым к исполнению и ждатьосвобожденияпроцессора

BLOCKED Блокирован Ожидает освобождения ресурса. Когда другие потоки

Page 292: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

освободят синхронизированн ый ресурс, поток переходит в состояние работоспособный

WAITING Ожидающий Переходит в это состояние по методам wait(), join() без параметров или с параметром 0. Ожидает сигнала, выданного методами notify(), notifyAll() или событием окончания потока

TIMED WAITI NG

Временноожидающий

Переходит в это состояние по методам wait(t), join(t), где t > 0 - время ожидания сигнала. Ожидает сигнала, выданного методами notify(), notifyAU() или событием окончания потока или истечения указанного интервала времени

TERMINATED Завершен Переходит в

Page 293: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Рис. 23. Схема переходов состояний потока.

Приоритеты потоков. Управление приоритетами

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

Page 294: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

процессоры, которые могут одновременно исполнять два потока команд. Большее число процессоров для обычных компьютеров остается пока экзотикой. Поэтому время центрального процессора - самый ценный ресурс вычислительной установки и распределение времени центрального процессора - одна из главных задач любой операционной системы. Для управления порядком предоставления времени центрального процессора потокам введено понятие «приоритета». Приоритет - это просто целое число в диапазоне от MIN_PRIORITY = 1 доMAX_PRI ORIТY = 10. Если имеется свободный квантвремени, то он отдается работоспособному потоку с наибольшим приоритетом. Если приоритеты работоспособных потоков одинаковы, то они получают кванты времени по очереди.

Рассмотрим проект EqualsPriorities, расположенный в директории Chapterl 1. Он состоит из описания трех классов.

Первый из описываемых классов NewThread предназначен для создания потока. В потоке исполняется метод шп(), который занят исключительно тем, что в цикле увеличивает счетчик. При этом он каждый раз проверяет: не поступил ли от главной программы сигнал об окончании работы? Если такой сигнал поступил, то метод выводит на печать накопленное значение счетчика и завершает свою работу. Важно, что метод шп() никакой другой работы не выполняет, и по значению счетчика можно оценить, сколько процессорного времени было выделено данному потоку.public class NewThread implements Runnable!

private Flag flag; private long counter = 0; private int number; private Thread t;public NewThread(Flag flag, int number) {

this.flag = flag; this.number = number; t = new Thread(this);//t.setPriority(number); t.start ();

Page 295: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public void run() {while (flag.getFlag()){

counter ++;}System.out.println("Поток " + number

+ " counter = " + counter);}

Класс Flag является вспомогательным. В программе создается один объект этого класса. Этот объект передается в качестве параметра каждому создаваемому объекту класса NewThread. Метод шп() опрашивает этот объект, вызывая метод getFlag() в цикле, на предмет продолжения работы. Главная программа вызывает один раз метод stop(), чтобы остановить все порожденные процессыpublic class Flag {

private boolean cont = true;public FlagO {}public boolean getFlag(){

return cont;}public void stop(){

cont = false;

В классе Main сосредоточена работа по созданию потоков и по управлению ими. Описывается и создается массив классов NewThread. Главный поток устанавливает себе максимально возможный приоритет, чтобы гарантированно получить управления после создания дочерних потоков. В цикле создаются объекты класса NewThread и помещаются в массив. После чего главный поток «засыпает» на 100 миллисекунд, и после этого выдает команду порожденным потокам на окончание работы.public class Main {

public static void main(String[] args) (

Page 296: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// количество потоков int SIZE = 9;// управление остановкой потоков Flag flag = new Flag О ;// создание массива потоковNewThread [] threads = new NewThread[SIZE];// установка максимального приоритета для // главного потока Thread.currentThread()

.setPriority(Thread.MAXJPRIORITY);// перехват прерывания метода sleep() try{

// создание массива потоков for(int i = 0; i < SIZE; i++){

threads[i] = new NewThread(flag, i + 1);}// останов на 1 секундуThread.currentThread().sleep(100);// команда стоп всем потокам flag.stop () ;

}catch(InterruptedException e){// печать сообщения об исключиельной ситуации

System.out.println(e.getMessage());}

}

Ниже приведен результат одного из запусков программы. Он показывает, что за выделенное время все потоки успевают получить управление и поработать.Поток 9 counter = 8547284Поток 7 counter = 54270383Поток 5 counter = 78081306Поток 4 counter = 77668693Поток 8 counter = 28957712Поток 6 counter = 57430972Поток 2 counter = 92502827Поток 3 counter = 135372335Поток 1 counter = 16450

Картина резко меняется, если строить потоки с разными приоритетами. Для этого внесем в конструктор класса NewThread небольшое изменение. После создания потока установим ему приоритет равный переданному ему числу. Конструктор будет выглядеть следующим образом:

public NewThread(Flag flag, int number) { this.flag = flag; this.number = number;

Page 297: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

t = new Thread(this);*t.setPriority{number); t. start () ;

>Теперь все потоки имеют разные приоритеты от 1 до 9.

Результаты запуска программы будут выглядеть следующим образом:Поток 9 counter = 24208281

Поток 8 counter = 19212015 Поток 7 counter = 5991768 Поток 6 counter = 8033 Поток 5 counter = 7320 Поток 4 counter = 280 Поток 3 counter = 0 Поток 1 counter = 0

Поток 2 counter = 0Поток с наивысшим приоритетом успевает выполнить

24 208 281 циклов работы, потоки с приоритетом 1, 2 и 3 не получают управления вовсе.

Приостановка потоковОбычной ситуацией является ситуация, когда один

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

Рассмотрим проект JoinDemo, находящийся в директории Chapter 11.class NewThread implements Runnable{ private Thread t; private int number; private int counter;public NewThread(int number, int counter){

this.number = number; this.counter = counter;

Page 298: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

t = new Thread(this); t .start ();System.out.println("Поток

+ number+ " создан; counter = " + counter);

public void run()( int k = 0;for (int i = 0; i < counter; i++)( k+ + ;

}System.out.println("Поток "

+ number+ " завершается");

public void joint) throws InterruptedException { t.join();

public class JoinDemo (public static void main(String[] args) {

// количество создаваемых потоков int SIZE = 10;// массив для хранения объектов NewThread [] threads = new NewThread[SIZE];II заполнение массиваfor (int i = 0; i < SIZE; i++)(threads[i] = new NewThread(i, IntRandom(1000000))

}// ожидание окончания потоков for (int i = 0; i < SIZE; i++){ try(threads[i].join();System.out.println("Поток "

+ i + " завершился");)catch(InterruptedException e)(System.out.println("Поток "

+ i + " завершился аварийно "+ e.getMessage());

}

}

private static int IntRandom(int diapason)} return (int)(diapason * Math.random());

Page 299: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Обратите внимание, что поток t является приватным свойством объекта класса NewThread. Поэтому из класса Main нельзя напрямую выдать метод t.join(). Чтобы это было возможно, в классе NewThread определен метод public void join(), который играет роль посредника между внешним потоком и потоком t.

Рассмотрим результаты одного из запусков этой программы.Поток 0 создан; counter = 565522

Поток 1 создан; counter 949738Поток 2 создан; counter * 305354Поток 3 создан; counter = 699991Поток 4 создан; counter 319795Поток 5 создан; counter * 271749Поток 6 создан; counter = 628424Поток 7 создан; counter = 929267Поток 8 создан; counter 592814Поток 9 создан; counter = 622517Поток 0 завершается Поток 0 завершился Поток 2 завершается Поток 4 завершается Поток 6 завершается Поток 1 завершается Поток 1 завершился Поток 2 завершился Поток 8 завершается Поток 5 завершается Поток 3 завершается Поток 9 завершается Поток 7 завершается Поток 3 завершился Поток 4 завершился Поток 5 завершился Поток 6 завершился Поток 7 завершился Поток 8 завершился Поток 9 завершился

Анализ результатов работы программы показывает, что потоки завершаются в произвольном порядке, но главная программа ждет завершения потоков в строго определенном порядке. После завершения потока 0, который случайно завершился первым, завершаются потоки 2, 4, 6 и только

Page 300: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

потом завершается поток 1. Главная программа переходит в состояние ожидания, пока поток 1 не завершится, но зато потом она не ждет завершения потока 2, поскольку он уже завершился и останавливается, ожидая окончания потока 3. В конце программа не останавливается, поскольку к моменту завершения 3-го потока все остальные потоки уже завершились.

Можно модифицировать нашу программу и не выдавать метод join(), если поток уже завершился. Для проверки завершения потока существует метод public boolean isAlive(), который выдает true, если поток не завершился и false в противном случае.

Для демонстрации применения метода isAlive рассмотрим проект AliveDemo, находящийся в директории Chapterll. Он является небольшой модификацией предыдущего проекта, поэтому остановимся только на том фрагменте проекта, который отличается от предыдущего.

Во-первых, дополняем описание класса NewThread описанием методаpublic boolean isAliveО {

return t. isAlive О;}

Во-вторых, цикл ожидания окончания потоков запишем в следующем виде:

if (!threads[i].isAlive()){System.out.println("Поток "

+ i+ " уже завершился");

)else 1System.out.println("Поток "

+ i+ " ждем завершения");

threads[i].join();System.out.println("Поток "

+ i+ " завершился");

}Протокол работы программы будет выглядеть

следующим образом:

Page 301: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Поток 0 Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток Поток

создан; counter =создан; counter создан; counter создан; counterсоздан;создан;

countercounter

6 создан; counter7 создан; counter8 создан; counter9 создан; counter 0 ждем завершения 0 завершается0 завершился1 ждем завершения2 завершается 1 завершается1 завершился2 уже завершился4 завершается6 завершается8 завершается9 завершается3 завершается5 завершается7 завершается3 ждем завершения3 завершился4 уже завершился5 уже завершился6 уже завершился7 уже завершился8 уже завершился9 уже завершился

441085 = 86035 = 96720 = 703264 = 679976 = 178109. = 133692 = 378417 = 368846 = 309584

Анализ результатов работы программы показывает, что использование метода isAlive() позволяет избежать выдачи метода join(), если поток уже завершился.

Обработка общих данных - неизбежная проблема при работе с потоками. Понятие критического ресурса. Синхронизированные блоки

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

Page 302: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Page 303: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Для демонстрации возникновения ситуации одновременного доступа рассмотрим проект Unsynchronized, находящийся в директории Chapter 11.

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

Главная программа - класс Factory моделирует работу производственного участка, имеющего SIZE станков. Все станки однотипные, образуют массив section, обрабатывают всего одну деталь, имеющую имя counter, которая представлена классом Counter. Объект counter является в данном проекте критическим ресурсом. Главная программа начинает свою работу созданием объекта класса Counter. Следующим шагом создается объект flag класса Flag, используемый для выдачи сигнала окончания работы. После этого главная программа создает массив section и заполняет его объектами класса Machine. Для заркска станков не требуется отдельного шага, они начинают свою работу сразу после создания объекта класса Machine, и завершают по сигналу главной программы. Сигнал подается путем вызова метода stop() объекта flag. Рабочий цикл станка состоит из обработки и периода, когда станок не работает с деталью. Обработка заключается в том, что каждый станок берет у

Page 304: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Класс Counter имеет одно свойство - целый счетчик counter и два метода public int getCounter() и public void setCounter(int value):public class Counter {

private int counter = 0;public int getCounter(){

System.out.println("Запросили counter = "+ counter);

return counter;

public void setCounter(int newCounter)( System.out.println("Установили counter =

+ newCounter); counter = newCounter;

Класс Flag уже был описан ранее. В этом проекте он использован без изменения:public class Flag {

private boolean flag = true;public boolean getFlag(){

return flag;

public void stop(){ flag' = false;

)

Page 305: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Класс Machine моделирует работу отдельного станка. Ядром программы является метод шп(), который исполняется в потоке. Он содержит цикл, в котором условием продолжения является flag.getFlag() == true. В теле цикла выделено два участка: обработка и периоднеактивности, который в жизни соответствует времени перехода станка от обработки одной детали к другой. Не важно, что у нас всего одна деталь в цеху, пока мы должны научиться справляться с одной, а потом усложним модель.public class Machine implements Runnable {

private Thread t; private Flag flag; private Counter counter; private int number;public Machine(Flag f, int n, Counter c) {

flag = f; number = n; counter = c; t = new Thread(this) ; t .start () ;

}public void run() {

System.out.println("Поток "+ number

+ " запушен"); while (flag.getFlag()){

try{// обработка(int w = counter.getCounter();System.out.println{"Поток "

+ number + " получил "+ w) ;

Thread.sleep(intRandom(20)) ; w++;counter.setCounter(w);System.out.println("Поток "

+ number + " установил "+ w) ;

}// период неактивности Thread.sleep(intRandom(20));

}catch(InterruptedException e)(

Page 306: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System, out .println ("Станок "+ number + " ошибка "+ e .getMessage ());

>}System.out.println("Поток "

+ number+ " завершается");

}public void joint) throws InterruptedException {

t.join();

private int intRandom(int i) { return (int)(i * Math.random());

Класс Factory является главной программой проекта, и ее логика описана выше.public class Factory {

public static void main(String[] args) {// обрабатываемый объект Counter counter = new Counter();// синхронизирующий флаг Flag flag = new FlagO;// количество станков int SIZE = 4;// описание станковMachine [] section = new Machine[SIZE];// создание станковfor (int i = 0; i < SIZE; i++){

section[i] = new Machine(flag, i, counter);}// ожидание конца работы try{

Thread.sleep(100); flag.stop();for (int i = 0; i < SIZE; i++)(

section[i].joint);)

}catch (InterruptedException e){System.out.println("Ошибка ожидания "

+ "конца работы: "+ е .getMessage());

}

Page 307: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Результат одного из запусков программы представлен ниже:Поток 0 запушен

Запросили counter = О Поток 0 получил О Поток 1 запушен Запросили counter = О Поток 1 получил 0 Поток 3 запушен Запросили counter = 0 Поток 3 получил О Поток 2 запушен Запросили counter = 0 Поток 2 получил 0 Установили counter = 1 Поток 3 установил 1 Установили counter = 1 Поток 1 установил 1 Установили counter = 1 Поток 2 установил 1 Установили counter = 1 Поток 0 установил 1 Запросили counter = 1 Поток 2 получил 1 Запросили counter = 1 Поток 3 получил 1 Запросили counter = 1 Запросили counter = 1 Установили counter = 2 Установили counter = 2 Поток 1 получил 1 Поток 3 установил 2 Поток 0 получил 1 Поток 2 установил 2 Поток 2 завершается Установили counter = 2 Поток 1 установил 2 Поток 3 завершается Установили counter = 2 Поток 0 установил 2 Поток 1 завершается Поток 0 завершается

Как и ожидалось, результат нас не удовлетворяет. Одно и то же значение счетчика передается в разные потоки, эти потоки, получив одинаковые значения, каждый увеличивает значение счетчика на 1 и помещает результат в объект

Page 308: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Один из способов доступа к монитору - использование синхронизирующего оператора, который имеет следующий синтаксис:synchronized(<объект>){<операторы>}

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

Доработаем наш проект одним оператором synchronized(){...} в методе run() в классе Machine, заключив обработку в синхронизированный блок.public void run() {

System.out.println("Поток "+ number

+ " запушен");while (flag.getFlagO ) {

try {// обработка synchronized(counter)(int w = counter.getCounter();System.out.println("Поток "

Page 309: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

+ number + " получил "+ w) ;

Thread.sleep(intRandom(20));w++ ;counter.setCounter(w);System.out.println("Поток "

+ number + " установил "+ w) ;

}// период неактивности Thread.sleep(intRandom(20));

}catch(InterruptedException e){System.out.println("Станок "

+ number + " ошибка "+ e.getMessage());

}}System.out.println("Поток "

+ number+ " завершается");

}После проведенной доработки запуск программы дает

следующий результат:Поток 0 запушен

Запросили counter = 0 Поток 0 получил 0 Поток 2 запушен Установили counter = 1 Поток 0 установил 1 Запросили counter = 1 Поток 2 получил 1 Поток 1 запушен Поток 3 запушен Установили counter = 2 Поток 2 установил 2 Запросили counter = 2 Поток 1 получил 2 Установили counter = 3 Поток 1 установил 3 Запросили counter = 3 Поток 3 получил 3 Установили counter = 4 Поток 3 установил 4 Запросили counter = 4 Поток 0 получил 4 Установили counter = 5

Page 310: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Поток 0 установил 5 Запросили counter = 5 Поток 2 получил 5 Установили counter = 6 Поток 2 установил б Запросили counter = 6 Поток 1 получил 6 Установили counter = 7 Поток 1 установил 7 Запросили counter = 7 Поток 3 получил 7 Установили counter = 8 Поток 3 установил 8 Запросили counter = 8 Поток 0 получил 8 Установили counter = 9 Поток 0 установил 9 Запросили counter = 9 Поток 2 получил 9 Поток 3 завершается Установили counter = 10 Поток 2 установил 10 Поток 0 завершается Запросили counter = 10 Поток 1 получил 10 Поток 2 завершается Установили counter * 11 Поток 1 установил 11 Поток 1 завершается

Анализ результатов работы программы ясно показывает, что потоки получают доступ к критическому ресурсу counter строго последовательно и корректно модифицируют счетчик в объекте counter.

Синхронизированные методы , как один из приемов синхронизации работы потоков.

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

Page 311: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public synchronized void process(int number)!

}Правила исполнения синхронизированных методов:• каждый класс может содержать любое количество

синхронизированных методов;• все синхронизированные методы одного объекта

исполняются последовательно.Монитор считает критическим ресурсом каждый

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

Рассмотрим проект SynchronizedMethod, находящийся в директории Chapter 11, Он делает в точности то же самое, что и программа проекта Unsynchronized. Проект состоит из тех же классов, что и Unsynchronized. Классы Flag и Factory ничем не отличаются от аналогичных в проекте Unsynchronized, поэтому не будем рассматривать их отдельно. Остановимся на классах Machine и Counter.

Начнем с класса Machine, описывающего работу станка. Станков в нашем примере SIZE = 4 экземпляров. Для описания процесса обработки надо создать синхронизированный метод, как показано выше. Но если описать его в классе Machine, то критическим ресурсом станет каждый станок, у нас появится SIZE критических ресурсов, работающих над одним некритическим - counter, что неверно. Чтобы сделать критическим ресурсом объект класса Counter, синхронизированный метод process() должен быть описан в нем.public class Machine implements Runnable {

private Thread t; private Flag flag; private Counter counter; private int number;public Machine(Flag f, int n, Counter c) {

flag = f;number = n;

Page 312: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

counter = с; t = new Thread(this); t. start () ;

public void run() {System.out.println("Поток "

+ number + " запушен");

while (flag.getFlagO){ try {

// обработкаcounter.process(number);// период неактивности Thread.sleep(intRandom(20));

)catch(InterruptedException e)i System.out.println("Станок "

+ number + " ошибка "+ e.getMessage());

}1System.out.println("Поток "

+ number+ " завершается");

public void joint) throws InterruptedException { t.join();

public static int intRandom(int i) { return (int)(i * Math.random());

1

Наглядно видно, что метод run(), исполняемый в отдельном потоке, стал даже короче за счет того, что процесс обработки свелся к вызову одного метода counter.process(number);

Сам процесс обработки описан в классе Counter:public class Counter {

private int counter = 0;public int getCounter()(

System.out.println("Запросили counter = "+ counter);

return counter;

Page 313: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

}public void setCounter(int newCounter){

System.out.println("Установили counter = " + newCounter);

counter = newCounter;}public synchronized void process(int number)

throws InterruptedException { int w = counter;System.out.println("Поток "

+ number + " получил "+ w);

Thread.sleep(Machine.intRandom(20)); w++;counter = w;System.out.println("Поток "

+ number + " установил " + w) ;

)Исполнение проекта дало правильные результаты, как

в доработанном варианте проекта Unsyncronized.

ВыводыКритическими ресурсами называются ресурсы, не

допускающие одновременной коррекции их из разных потоков.

Критическими ресурсами в языке Java являются отдельные объекты.

Язык Java использует встроенные мониторы для синхронизации доступа к критическим ресурсам. Монитор следит, чтобы доступ к критическому ресурсу в каждый момент времени был предоставлен только одному потоку.

В языке Java реализовано два метода использования мониторов:

• через синхронизированные блоки;• через синхронизированные методы.

Page 314: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

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

Реализация семафоров - важнейший прием регулирования доступа к критическим ресурсам.Синхронизирующие методы wait() и notify()

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

• пустой• заполненныйНаш производственный участок схематически показан

на рис. 24.

Page 315: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

РИС. 24. Производственный участок из двух станков и лотка для деталей.

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

Для решения этой задачи привлечем методы, присутствующие в любом объекте public void wait() и public void notify().

Правила работы методов wait(), notify() и notifyAll():• все эти методы должны вызываться из

синхронизированных методов или синхронизированных блоков;

• метод wait() освобождает монитор и переводит текущий поток в состояние ожидания;

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

• метод notifyAll() переводит все потоки, ожидающие данный монитор в работоспособное состояние. Сам поток, выдавший этот метод, не прерывает своего исполнения. По окончании работы метода, вызвавшего метод notifyAHQ, исполнительная система отдает монитор

Page 316: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Рассмотрим реализацию описанной модели, приведенную в проекте ProducerConsumer, находящемся в директории Chapter 11.

Центральным классом, выполняющим всю работу по синхронизации потоков, является класс Buffer:public class Buffer {

// состояние лотка пуст/полон == false/true private boolean full = false;// деталь, лежащая в лотке private Detail detail;// признак продолжения работы // работа продолжается до тех пор,// пока flag == true private boolean flag;// конструктор public Buffer!) {

// установить флаг в положение "работает" flag = true;

}// метод, помещающий очередную деталь в лоток public synchronized void put(Detail detail)!

try{// ожидание, когда лоток освободитсяI I Выйти из цикла ожидания можно двумя способами// освободить лоток (full == false)I I или закончить смену (flag == false) while (full && flag) {

wait ();)I I остановить признак "лоток занят" full = true;// получить деталь в лоток this.detail = detail;// активизировать все ожидающие потоки notifуА11();

}catch(InterruptedException е)( е.printStackTrace();

}>// метод, выдающий очередную деталь из лотка public synchronized Detail get(){

try{

Page 317: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// ожидание поступления в лоток очередной I I детали// для того чтобы попасть в состояние // ожидания, должно быть выполнено два // условия:// - лоток должен быть пуст // - рабочее время не должно закончиться while {!full && flag){

wait ();>// установить признак "лоток пуст" full = false;// сделать работоспособными все потоки,// ожидающие доступа к лотку notifyAll();// вернуть результат return detail;

)catch(InterruptedException e){ e .printStackTrace(); return null;

1}// устанавливает признак окончания работы public synchronized void stop(){

// сбросить признак продолжения работы flag = false;// активизировать все потоки, ожидающие // доступа к лотку, чтобы они опросили признак // продолжения работы и вышли из монитора.// если этого не сделать, то те процессы,// которые находились в состоянии ожидания // не узнают о том, что признак продолжения // работы изменился, и программа не сможет // нормально завершиться notifyAll();

// метод выдает значение признака продолжения // работы. Работать/остановиться == true/false public boolean getFlagО {

return flag;

}Класс Producer описывает работу

производящего детали:public class Producer implements Runnable {

// лоток обмена деталями. В него производитель // кладет произведенные детали

станка,

Page 318: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

private Buffer buffer;// поток, в котором работает станок private Thread t;// конструктор

public Producer(Buffer b) {// запомнить лоток buffer = b;I I создать поток t = new Thread(this) ;I I запустить поток t .start ();

public void run() { try{

System.out.println("Производитель запущен");// основной рабочий цикл while (buffer.getFlag()){

// имитировать производство детали Thread.currentThread()

.sleep(Utils.intRandom(20));Detail detail = new Detail!);System.out.println(detail + " изготовлена"); I I положить деталь в лоток buffer.put(detail);System.out.println(detail + " передана");

)System.out.println("Производитель завершается

(catch(InterruptedException e)( e.printStackTrace();

I I метод ожидания окончания работы производителя public void join() throws InterruptedException {

t.joint);

)Класс Consumer описывает работу

перерабатывающего произведенные деталиpublic class Consumer implements Runnable {

// лоток обмена деталями. Из него потребитель // берет детали на переработку private Buffer buffer;// поток, в котором работает станок private Thread t;// конструктор

станка

Page 319: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public Consumer(Buffer b) {// запомнить лоток, через который будет идти работа buffer = b;// создать ноавй поток t new Thread(this);// запустить поток t .start ();

public void run() { try{

System.out.println("Потребитель запущен");// основной рабочий цикл while (buffer.getFlagO ) {

// получение детали из лотка Detail detail = buffer.get О ;System.out.println(detail + " получена”);// имитация преработки детали потребителем Thread.sleep(Utils.intRandom(20));System.out.println(detail + " переработана");

}System.out.println("Потребитель завершается");

}catch(InterruptedException e){ e.printStackTrace();

// метод ожидания окончания работы станка public void join() throws InterruptedException {

t.joint);

)Класс Factory создает модель цеха и организует его

работуpublic class Factory {

public static void main(String[) args) {// создать лоток для обмена деталями Buffer buffer = new Buffer О;// создать и запустить станкиProducer producer = new Producer(buffer);Consumer consumer = new Consumer(buffer); try{

// ждать конца рабочей смены Thread.sleep(100);I I дать сигнал останова производства buffer.stop();// ждать завершения работы всех станков producer.join(); consumer.j oin();

Page 320: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

System.out.println("Главная программа завершается");

}catch(InterruptedException e){ e.printStackTrace();

)}

Класс Detail описывает производимые детали.public class Detail {

// глобальный счетчик выпущенных деталей private static int counter = 0;// уникальный номер детали private int number;// конструктор public Detail() {

// установить уникальный номер детали number = ++ counter;

}// метод, выдающий писание детали public String toString(){

return "Деталь S' " + number;

}Класс Utils содержит полезный метод получения

случайного целого числа:public class Utils {

public static int intRandom(int diapason){ return (int)(Math.random() * diapason);

)

Приведем результат оного из запусков нашей программыПотребитель запущен

Производитель запущен Деталь W 1 изготовлена Деталь № 1 передана Деталь № 1 получена Деталь N> 1 переработана Деталь № 2 изготовлена Деталь N> 2 передана Деталь № 2 получена Деталь N' 3 изготовлена Деталь N* 3 передана Деталь № 2 переработана

Page 321: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Деталь № 3 полученаДеталь № 3 переработанаДеталь № 4 изготовленаДеталь № 4 переданаДеталь № 4 полученаДеталь № 4 переработанаДеталь № 5 изготовленаДеталь № 5 переданаДеталь № 5 полученаДеталь № 5 переработанаДеталь № 6 изготовленаДеталь № 6 полученаДеталь И* 6 переданаДеталь № 6 переработанаДеталь № 7 изготовленаДеталь № 7 переданаДеталь № 7 полученаДеталь № 8 изготовленаДеталь № 8 переданаДеталь № 7 переработанаПотребитель завершаетсяДеталь № 9 изготовленаДеталь № 9 переданаПроизводитель завершается Главная программа завершается

Анализ результатов работы программы показывает, что детали последовательно создаются и через лоток переносятся со станка на станок

Простой двоичный семафорПредставим себе, что нам нужно организовать

движение деталей по цеху. Деталь, появившись на свет на станке Producer, движется слева направо к станку Consumer. По дороге она должна пройти через лоток Buffer. Поставим на пути детали два семафора, как показано на рис. 25.

Page 322: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Лоток своболен

Лоток занят

Семафороткрыт

Рис. 25. Организация движения деталей от производителя к потребителю с помощью семафоров.

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

Итак, мы изобретаем новый класс синхронизированных объектов Semaphore - семафоры. Каждый объект этого класса имеет два состояния: открыт и закрыт и реализует два метода public void entry() и public void exit(). Если семафор открыт, то метод entry() закрывает его и выходит из него. Если закрыт, то метод entry() преводит текущий поток в состояние ожидания до тех пор, пока семафор не откроется, когда он откроется, то он его закрывает и выходит из метода. Метод exit() проверяет: открыт ли семафор? Если открыт, то никаких действий не выполняет. Если семафор

Page 323: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

закрыт, то открывает его и активизирует все потоки, ждущие открытия этого семафора.

Рассмотрим проект Semaphore, расположенный в директории Chapter 11, реализующий ту же модель производства, что и проект ProducerConsumer.

Проект содержит описание нового класса Semaphore:public class Semaphore {

// состояние семафора открыт/закрыт == true/false private boolean opened;// флаг продолжения работы private boolean flag = true;// конструкторpublic Semaphore(boolean status) {

// установить начальное состояние семафора opened = status;

// войти в критический участок,// охраняемый семафором public synchronized void entry()

throws InterruptedException {// ждать открытия семафора while ((opened && flag){

wait () ;)// закрыть семафор opened = false;

}// выйти из критического участка,// охраняемого семафором public synchronized void exit(){

// если семафор закрыт, if (!opened){

// то открыть его opened = true;/ / и проинформировать ждущие потоки, // что семафор открылся notify();

)}public synchronized void stop О {

// установить флаг окончания работы flag = false;// проинформировать все ждущие потоки,

Page 324: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// что состояние флага изменилось notifyAll();

}}

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

// входной и выходной семафоры private Semaphore in, out;// место хранения передаваемой детали private Detail detail;// признак продолжения работы private boolean flag = true;// конструктор public Buffer() {

// создать входной семафор in = new Semaphore(true);// создать выходной семафор out = new Semaphore(false);

)

// метод, помещающий очередную деталь в лоток public void put(Detail detail){

try{// пройти входной семафор in.entry () ;// получить деталь в лоток this.detail = detail;// открыть выходной семафор out.exit();

)catch(InterruptedException e){ e.printStackTrace();

}// метод, выдающий очередную деталь из лотка public Detail get(){

try{// пройти выходной семафор out.entry();// взять возвращаемое значение Detail result = detail;// открыть входной семафор in.exit();// вернуть результат return result;

}catch(InterruptedException e){

Page 325: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

e.printStackTrace() ; return null;

}// устанавливает признак окончания работы public void stop(){

// установить признак окончания работы flag = false;// остановить входной семафор in.stop();// остановить выходной семафор out.stop();

}// метод выдает значение признака продолжения I I работы. Работать/остановиться == true/false public boolean getFlag() {

return flag;

Классы Producer, Consumer, Factory и Utils, а также результаты работы программы ничем не отличаются от описанных в проекте ProducerConsumer, поэтому мы их не приводим.

Целочисленные семафорыХарактерной особенностью лотка является то, что он

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

В начале работы лоток пуст.Метод public void put(Detail detail) работает следующим

образом. Если в лотке хранится 0, 1, ..., N - 1 деталь, то лоток принимает эту деталь. Если в лотке N деталей, то он переводит поток в состояние ожидания до тех пор, пока в нем ни освободится место для приема детали, после чего он кладет эту деталь в лоток. После того, как деталь попала в

Page 326: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

лоток, он информирует потоки, ожидающие получения детали, о поступлении детали.

Метод public Detail get() работает следующим образом. Если в лотке есть детали, то берем одну из них и возвращаем ее в качестве результата. Если нет, то переводим поток в состояние ожидания прихода детали. После прихода детали берем ее в качестве результата и информируем процессы, ожидающие свободного места в лотке для того, чтобы положить туда произведенные детали.

Усложним нашу модель производства. Пусть имеется int PRODUCERNUMBER однотипных станков, производящих детали, int CONCUMER_NUMBER однотипных станков, потребляющих детали и один лоток емкостью int BUFFERVOLUME. Все производители производят однотипные детали и кладут их в общий лоток. Все потребители берут детали для обработки из общего лотка. Вход от производителей охраняет семафор in, который открыт пока в лотке деталей меньше, чем BUFFER VOLUME и закрывается, как только в лотке оказывается BUFFER_VOLUME деталей. Вход справа охраняет семафор out, который открыт, когда в лотке есть хотя бы одна деталь и закрывается, если лоток пуст.

Рассмотрим проект подробнее.Класс Detail по-прежнему описывает произведенные

детали. Поскольку производителей стало несколько, то мы добавили свойство private int machine, которое должно хранить номер станка, изготовившего деталь. Это свойство передается в конструкторе и используется в методе public String toString(). В остальном класс Detail не отличается от аналогичного в предыдущих проектах. Ниже приводится его текст:public class Detail {

I I гобальный счетчик выпущенных деталей private static int counter = 0;I I уникальный номер детали private int number;// номер станка-производителя

Page 327: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

private int machine;1 1 конструктор public Detail(int m) {

// установить уникальный номер детали number = ++ counter;// установить номер станка производителя machine = m;

// метод, выдающий описание детали public String toString(){

return "Деталь Si " + number + " Произвел " + machine;

}

Класс Queu является новым классом. Он предназначен для организации очереди деталей на обработку. Ранее детали передавались по одной, и для хранения этой детали было достаточно одной переменной. Теперь в лотке может находиться любое количество деталей от 0 до BUFFERVOLUME. Поэтому необходимо разработать некоторую структуру данных и способы доступа к ней, чтобы обеспечить хранение и выдачу деталей по запросу. Поскольку к этой структуре могут асинхронно обращаться разные процессы, то методы доступа должны быть синхронизированными методами.public class Queu {

private Detail [] details; private int quantity = 0;public Queu(int size) {

details = new Detail [size];

public synchronized void put (Detail detail)( details[quantity++] = detail;

public synchronized Detail get(){ Detail result = details[0]; quantity— ;for(int i = 0; i < quantity; i++){ details [i] = details[i + 1) ;

}return result;

Page 328: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Охрану доступа к лотку осуществляют два семафора, но они должны обеспечивать доступ для помещения в лоток более одной детали, поэтому доступом к лотку управляет целочисленный семафор. Его внутренне свойство private int volume в каждый момент времени показывает: сколько потоков данный семафор может еще пропустить. Если значение свойства volume станоится равным 0, то семафор закрывается. Количество потоков, которое семафор может пропустить сразу после создания, предается в конструкторе.public class Semaphore {

II состояние семафора открыт/закрыт == true/false private int volume;II флаг продолжения работы private boolean flag = true;

// конструкторpublic Semaphore(int free) {

// установить начальное состояние семафора volume = free;

)II войти в критический участок,// охраняемый семафором public synchronized void entry!)

throws InterruptedException {П ждать открытия семафора while (volume d= 0 S4 flag){ wait ();

}// уменьшить количество свободного ресурса volume--;

)II выйти из критического участка,// охраняемого семафором public synchronized void exit О {

II увеличить количество свободного ресурса volume++;//и проинформировать ждущие потоки,// что семафор открылся notify();

}

public synchronized void stop О {

Page 329: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

II установить флаг окончания работы ‘flag = false;// проинформировать все ждущие потоки, // что состояние флага изменилось notifyAll();

}}

Теперь лоток стал сложнее: он предназначен для управления передачей через себя более одной детали в каждый момент времени, содержит два семафора и очередь. Логика поведения лотка читается в тексте класса Buffer:public class Buffer {

// входной и выходной семафоры private Semaphore in, out;// место хранения передаваемой детали private Queu queu;// признак продолжения работы private boolean flag = true;

// конструкторpublic Buffer(int size) {

// создать водной семафор in = new Semaphore(size) ;// создать выходной семафор out = new Semaphore(0);// создать место хранения деталей queu = new Queu(size);

// метод, помещающий очередную деталь в лоток public void put(Detail detail){

try{// пройти входной семафор in.entry();// получить деталь в лоток if (flag){queu.put(detail);

}// открыть выходной семафор out.exit();

}catch(InterruptedException e){ e.printStackTrace () ;

// метод, выдающий очередную деталь из лотка public Detail get()(

try{

Page 330: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

// пройти выходной семафор out.entry();// взять возвращаемое значение Detail result = null; if (flag)Iresult = queu.get();

}// открыть входной семафор in.exit();// вернуть результат return result;

}catch(InterruptedException e){ e.printStackTrace(); return null;

// устанавливает признак окончания работы public void stop(){

// установить признак окончания работы flag = false;// остановить входной семафор in. stop () ;// остановить выходной семафор out.stop();

}// метод выдает значение признака продолжения // работы, работать/остановиться == true/false public boolean getFlag() {

return flag;}

Логика производителя и потребителя изменилась очень мало. Единственно отличие заключается в том, что каждый из станков получил свой номер и при печати каждый раз указывает свой номер, чтобы была возможность понять, от какого именно станка поступила информация.public class Producer implements Runnable {

// номер станка private int number;

// лоток обмена деталями. В него производитель // кладет произведенные детали private Buffer buffer;// поток, в котором работает станок private Thread t;

II конструктор

Page 331: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public Producer(Buffer b, int n) { II запомнить лоток buffer = b;// запомнить номер станка number = n;// создать поток t = new Thread(this);// запустить поток t.start();

public void run() { try{System.out.println("Производитель "

+ number + " запущен");

// основной рабочий цикл while (buffer.getFlagО ) {

// имитировать производство детали Thread.currentThread()

.sleep(Utils.intRandom(20));Detail detail = new Detail(number);System.out.println(detail + " изготовлена") II положить деталь в лоток buffer.put(detail);System.out.println(detail + " передана");

}System.out.println("Производитель "

+ number+ " завершается");

}catch(InterruptedException e){ e.printStackTrace();

// метод ожидания окончания работы производителя public void join() throws InterruptedException { t.join();

!

public class Consumer implements Runnable {// номер потребителя private int number;// лоток обмена деталями. Из него потребитель // берет детали на переработку private Buffer buffer;// поток, в котором работает станок private Thread t;

// конструктор

Page 332: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

public Consumer(Buffer b, int n) {// запомнить лоток, через который будет идти работа buffer = b;// запомнить собственный номер number = п;// создать ноавй поток t = new Thread(this);// запустить поток t.start();

public void run() { try{System.out.println("Потребитель "

+ number + " запущен");

// основной рабочий цикл while (buffer.getFlag()Н

// получение детали из лотка Detail detail = buffer.get();System.out.println("Потребитель "

+ number^ It И

+ detail + " получена");

// имитация переработки детали потребителем Thread.sleep(Utils.intRandom(20));System.out.println("Потребитель "

+ number + " "+ detail+ " переработана");

)System.out.println("Потребитель ”

+ number+ " завершается");

}catch(InterruptedException e)( e.printStackTrace();

}

II метод ожидания окончания работы станка public void joint) throws InterruptedException {

t.join();

}Логика главной программы поменялась незначительно.

В тех местах, где раньше создавался один станок, там создается массив из однотипных станков, там, где раньше

Page 333: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

ждали завершения одного потока, там сейчас ждут завершения массива потоков и так далее, но в целом логика программы сохранилась.public class Factory {

public static void main(String[] args) { int BUFFER_VOLDME = 5; int PRODUCER_NUMBER = 3; int CONSUMER_NUMBER = 4;// создать лоток для обмена деталями Buffer buffer = new Buffer(BUFFER_VOLUME);// создать и запустить станки Producer [] producers =

new Producer[PRODUCER_NUMBER]; for (int i = 0; i < PRODUCER_NUMBER; i++)( producers[i] = new Producer(buffer, i + 1);

}Consumer [] consumers =

new Consumer[CONSUMER_NUMBER]; for (int i = 0; i < CONSUMER_NUMBER; i++)( consumers[i] = new Consumer(buffer, i + 1) ;

)try{

// ждать конца рабочей смены Thread.sleep(100);// дать сигнал останова производства buffer.stop();// ждать завершения работы всех станков for (int i = 0; i < PRODUCER_NUMBER; i++){ producers[i].join();

)for (int i = 0; i < CONSUMER_NUMBER; i++){ consumers[i].join();

}System.out.println

("Главная программа завершается");}catch(InterruptedException e)( e.printStackTrace();

}}

}Анализ результатов работы программы показывает, что

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

Page 334: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Производитель 2 запущенПотребитель 1 запущенПотребитель 3 запущенПроизводитель 3 запущенПотребитель 2 запущенПотребитель 4 запущенДеталь № 1 Произвел 3 изготовленаДеталь № 1 Произвел 3 переданаПотребитель 1 Деталь № 1 Произвел 3 полученаДеталь № 2 Произвел 1 изготовленаДеталь № 2 Произвел 1 переданаПотребитель 3 Деталь № 2 Произвел 1 полученаПотребитель 3 Деталь № 2 Произвел 1 переработанаДеталь № 3 Произвел 1 изготовленаДеталь № 3 Произвел 1 переданаПотребитель 2 Деталь № 3 Произвел 1 полученаДеталь № 4 Произвел 1 изготовленаДеталь № 4 Произвел 1 переданаПотребитель 4 Деталь № 4 Произвел 1 полученаДеталь № 5 Произвел 2 изготовленаДеталь № 5 Произвел 2 переданаПотребитель 3 Деталь № 5 Произвел 2 полученаДеталь № 6 Произвел 3 изготовленаДеталь № 6 Произвел 3 переданаДеталь № 7 Произвел 2 изготовленаДеталь № 7 Произвел 2 переданаПотребитель 1 Деталь № 1 Произвел 3 переработанаПотребитель 1 Деталь № 6 Произвел 3 полученаПотребитель 2 Деталь № 3 Произвел 1 переработанаПотребитель 2 Деталь № 7 Произвел 2 полученаПотребитель 4 Деталь № 4 Произвел 1 переработанаПотребитель 1 Деталь № 6 Произвел 3 переработанаДеталь № 8 Произвел 1 изготовлена Потребитель 4 Деталь № 8 Произвел 1 получена Деталь № 8 Произвел 1 передана Деталь № 9 Произвел 3 изготовлена Деталь № 9 Произвел 3 переданаПотребитель 2 Деталь № 7 Произвел 2 переработанаПотребитель 1 Деталь № 9 Произвел 3 полученаПотребитель 3 Деталь № 5 Произвел 2 переработанаДеталь № 10 Произвел 1 изготовленаПотребитель 2 Деталь № 1 0 Произвел 1 полученаДеталь № 10 Произвел 1 переданаДеталь № 11 Произвел 2 изготовленаДеталь № 11 Произвел 2 переданаПотребитель 3 Деталь № 11 Произвел 2 полученаПотребитель 1 Деталь № 9 Произвел 3 переработанаДеталь № 12 Произвел, 1 изготовленаДеталь № 12 Произвел 1 переданаПотребитель 1 Деталь № 12 Произвел 1 полученаДеталь № 13 Произвел 3 изготовлена

Page 335: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Деталь № 13 Произвел 3 переданаПотребитель 2 Деталь № 10 Произвел 1 переработанаПотребитель 2 Деталь № 13 Произвел 3 полученаПотребитель 4 Деталь № 8 Произвел 1 переработанаПотребитель 2 Деталь № 13 Произвел 3 переработанаДеталь № 14 Произвел 1 изготовленаДеталь № 14 Произвел 1 переданаПотребитель 4 Деталь № 14 Произвел 1 полученаПотребитель 3 Деталь № 11 Произвел 2 переработанаДеталь № 16 Произвел 3 изготовленаДеталь Я* 16 Произвел 3 переданаДеталь № 15 Произвел 2 изготовленаПотребитель 3 Деталь № 16 Произвел 3 полученаДеталь № 15 Произвел 2 переданаПотребитель 3 Деталь № 16 Произвел 3 переработанаПотребитель 3 Деталь № 15 Произвел 2 полученаПотребитель 3 Деталь № 15 Произвел 2 переработанаПотребитель 1 Деталь № 12 Произвел 1 переработанаПотребитель 4 Деталь № 14 Произвел 1 переработанаДеталь № 17 Произвел 2 изготовленаДеталь № 17 Произвел 2 переданаПотребитель 2 Деталь № 17 Произвел 2 полученаДеталь № 18 Произвел 2 изготовленаДеталь № 18 Произвел 2 переданаПотребитель 3 Деталь № 18 Произвел 2 полученаДеталь № 19 Произвел 1 изготовленаДеталь № 19 Произвел 1 переданаПотребитель 1 Деталь № 19 Произвел 1 полученаДеталь № 20 Произвел 2 изготовленаДеталь № 20 Произвел 2 переданаДеталь № 21 Произвел 3 изготовленаДеталь № 21 Произвел 3 переданаПотребитель 4 Деталь № 20 Произвел 2 полученаПотребитель 2 Деталь № 17 Произвел 2 переработанаПотребитель 1 Деталь № 19 Произвел 1 переработанаПотребитель 3 Деталь № 18 Произвел 2 переработанаДеталь № 22 Произвел 2 изготовленаДеталь № 22 Произвел 2 переданаПотребитель 1 Деталь № 22 Произвел 2 полученаПотребитель 1 Деталь № 22 Произвел 2 переработанаДеталь № 23 Произвел 1 изготовленаДеталь № 23 Произвел 1 переданаДеталь № 24 Произвел 3 изготовленаДеталь № 24 Произвел 3 переданаПотребитель 4 Деталь № 20 Произвел 2 переработанаПотребитель 4 Деталь № 23 Произвел 1 полученаПотребитель 2 Деталь № 21 Произвел 3 полученаПотребитель 3 Деталь № 24 Произвел 3 полученаДеталь № 25 Произвел 3 изготовленаДеталь № 25 Произвел 3 переданаПотребитель 1 Деталь № 25 Произвел 3 получена

Page 336: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Потребитель 4 Деталь № 23 Произвел 1 переработанаПотребитель 1 Деталь № 25 Произвел 3 переработанаДеталь № 26 Произвел 3 изготовленаДеталь № 26 Произвел 3 переданаПотребитель 4 Деталь № 26 Произвел 3 полученаДеталь № 27 Произвел 3 изготовленаДеталь № 27 Произвел 3 переданаПотребитель 1 Деталь № 27 Произвел 3 полученаДеталь № 28 Произвел 2 изготовленаДеталь № 28 Произвел 2 переданаПроизводитель 2 завершаетсяПотребитель 4 Деталь № 26 Произвел 3 переработана Потребитель 4 завершаетсяПотребитель 2 Деталь № 21 Произвел 3 переработана Потребитель 2 завершаетсяПотребитель 1 Деталь № 27 Произвел 3 переработана Потребитель 1 завершаетсяПотребитель 3 Деталь № 24 Произвел 3 переработанаПотребитель 3 завершаетсяДеталь № 2 9 Произвел 1 изготовленаДеталь № 29 Произвел 1 переданаПроизводитель 1 завершаетсяДеталь № 30 Произвел 3 изготовленаДеталь № 30 Произвел 3 переданаПроизводитель 3 завершаетсяГлавная программа завершается

ВыводыУправление асинхронными процессами - одна из

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

Page 337: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

ЗаключениеНа этом мы завершаем рассмотрение основ

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

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

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

Мы не могли рассмотреть в данной книге методы пректирования больших программных комплексов, таких

Page 338: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

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

Page 339: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Литература1. Никлаус Вирт. Алгоритмы и структуры данных.

«Невский диалект» СПб 2008.2. Патрик Нотой, Герберт Шилдт. Полный

справочник по Java. «Диалектика», М. 1997.3. Кей Хорстман, Гарри Корнелл. Java 2. Том 1.

Основы, Москва, Санкт-Петербург, Киев, 2008.4. Майкл Ленди, Селим Сиддикви, Джефф Свишер и

др. Borland JBuilder. Руководство разработчика. Издательский дом «Вильямс» Москва, Санкт-Петербург, Киев, 2008.

Page 340: library.miit.rulibrary.miit.ru/methodics/10-1525.pdf · УДК 004 Мб 9 МихайлюкА.В. Введение в объектно-ориентированное программирование.

Св. план 2009г., поз. 69

Михайлюк Алексей Викторович

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

Учебное пособие

Подписано к печати -S O . 12.. 0 3 . Формат 60 х 84 1/16

Тираж 100 экз. Заказ - 9 % О-

Усл.-печ.л. -21,25____________________________127994, Москва, ул. Образцова, 9, стр. 9

Типография МИИТа