Создание сложных проектов

Э

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

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

В следующей главе я расскажу о системах управления версиями (CVS) и команд­ном методе разработки. Следующая глава будет последней главой, посвященной со­зданию проектов и управлению проектами в JBuilder. После нее подробно будут рассмотрены вопросы развертывания.

Но перед тем как перейти к рассмотрению технических вопросов, хотелось бы уделить внимание следующим двум моментам:

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

■ Некоторые вопросы, рассматриваемые в настоящей главе, могут быть отнесе­ны к темам повышенного уровня сложности. Если вы мало знакомы с языком программирования Java и хотите для начала просто научиться создавать при­ложения, возможно, некоторые разделы главы будет лучше пропустить. C другой стороны, если вы ставите перед собой цель научиться создавать слож­ные Java-приложения, или хотите с помощью среды JBuilder работать со слож­ными приложениями, эта глава как раз для вас.

Сложные проекты

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

■ Пр эекты со сложными путями классов. В приложениях такого рода файлы ис-

XOj ного кода находятся в разных каталогах.

■ Приложения, содержащие локальные пакеты проекта, т. е. пакеты являются подкаталогами основного каталога проекта.

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

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

примечаниеВ конечном счете, все утилиты, разработанные в этой главе и других главах кни­ги, будут сохранены в файле с именем codeboχ. jar. Файл codeboχ. jar, а также его исходные коды входят в состав сопровождающих книгу материалов. Он распростра­няется в соответствии с лицензией Mozilla и доступен для разработчиков про­граммного обеспечения по всему миру. Таким образом, разрабатываемые утилиты станут примером не только создания пакетов многократного использования, но по­зволят вам научиться устанавливать и использовать пакеты.

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

В Web можно найти огромное количество кода и большинство из него можно на­звать не просто хорошим, а великолепным. Умение использовать программные средства независимых разработчиков является одним из обязательных навыков лю­бого программиста на Java. Важным дополнением к этой информации станет описа­ние JAR-файлов, приведенное в главе 28 и в других главах книги.

Добавление, удаление и обновление файлов проекта

В главе 4 была рассмотрена панель проекта. Мне хотелось бы сказать еще не­сколько слов о панели проекта, поскольку она имеет прямое отношение к материа­лу настоящей главы.

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

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

*∙**Λ*w-βιaι∣uu Если выполнить щелчок правой кнопкой мыши на корневом узле в па­нели проекта, можно заметить, что контекстное меню дает возможность создавать новый каталог. Этот каталог будет "виртуальным", т. е. будет частью файла проекта, а не реаль­ным каталогом операционной системы, входящим в структуру дерева каталогов проекта. Создаваемые виртуальные каталоги используются для организации кода в среде JBuiIder, однако они не позволяют создавать пакеты Java.

Для удаления файлов предусмотрено две возможности. Первая называется ис­ключением (removing) файла, а вторая — удалением (deleting) файла. При исключе­нии файла из проекта он все равно остается в проекте, просто он не будет отобра­жаться в панели проекта, если вы не откроете пиктограмму пакета, в котором нахо­дится исключенный файл. При исключении файла из проекта он не удаляется из жесткого диска. Файл считается частью проекта, если пакет, содержащий файл, яв­ляется частью проекта. Отображение файла в панели проекта всего лишь обеспечи­вает более удобный метод доступа к файлу.

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

примечаниеБывают ситуации, когда список файлов в панели проекта не соответствует реаль­ному составу проекта. Например, в панели проекта могут содержаться пакеты, кото­рые были удалены с жесткого диска. В таком случае для приведения в соответствие отображаемого и реального содержимого проекта используется кнопка Refresh, рас­положенная в панели инструментов, или пункт меню Project J Refresh. Если, напри­мер, структура проекта была изменена с помощью проводника Windows, KDE Konquerer или любого другого диспетчера файлов, перед проведением других опера­ций в среде JBuilder проект потребуется обновить.

………. При внесении изменении в каталоги или файлы пакетов, расположен­ные в зге, среда JBuiIder всегда будет вносить соответствующие изменения в каталоги классов. При этом в некоторых случаях JBuiIder выводит непонятные сообщения об ошибках, или код, который уже удален из проекта, продолжает присутствовать в коде приложения. Если такие ошибки возникают или если вы предполагаете, что они могут возникнуть, попробуйте удалить все подкаталоги в каталоге классов. Удаляйте не просто файлы .class, а всю структуру каталогов. Будьте особенно внимательны при удалении каталогов кэша пакетов.

Bπv^ιM^TEιrur^

IavaawLAWTEvent

IavaawLAWTEventMunicaster

IavaawLAWTExceptIon

IavaawLAWTPermission

Lava. awLActlveEvent

Ava.8wLAdjustable

Ava. awLAlphaCompostte

IavaawLAlphacomposlteContext

Iava-SwLAttnbuteVaIue

IavaawLEasicStroke

Iava-SwLBaslcStroketFinAdapter java SwLBorderLayout lava awLButton

T ‘⅜~⅜⅜ jt∫

JlL……………………… —

*1cjdssinsigbtsearehtoc ∣∣ava.awtmatchlngtlstoptions
г insert short class name wtth explicit ∣rnport Γ insert short class name with package import
рис. 25.1. просмотр в jbuilder классов, доступных для использования

JBuiIder и пакеты

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

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

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

Список всех классов определенного проекта можно просмотреть, выбрав в меню Jbuilder пункт Search ∣ Find Classes. Кроме того, вы можете начать набирать опера­тор Import, А затем нажать комбинацию клавиш Ctrl+Alt+пробел, которая вызовет на экран диалоговое окно. Если в появившемся диалоговом окне выбрать класс и нажать кнопку OK, имя класса будет автоматически добавлено в исходный код. Вне­шний вид диалогового окна представлен на рис. 25.1

Автоматическое импортирование пакетов в JBuiIder

При введении в программу новой переменной в некоторых случаях чтобы тип переменной распознавался Java приходится добавлять оператор import. Например, если вы создадите стандартное приложение JBuilder и объявите в нем переменную типа ArrayList, вам придется в список оператора import добавить пакет java. util. ArrayList или java. util.*. Импортирование необходимо, поскольку КОД описания класса ArrayList находится В классе java. util. ArrayList.

Диалоговое окно ClassInsight, показанное на рис. 25.1, очень удобно для добав­ления в проект списков импортирования. В Частности, его можно использовать для поиска параметра оператора import и его автоматической вставки.

Предположим, например, что вы добавляете следующий метод к потомку класса JFrame C именем Framel:

Void jButtonl_actionPerformed(ActionEvent Е) {

ArrayList List;

Приведенный выше код не будет автомати­чески компилироваться, если в начале програм­мы отсутствует соответствующий оператор import. Очевидно, что проблема заключается в том, что зачастую разработчики не помнят, в каком именно Java-πaκeτe находится класс ArrayList.

В поиске нужного пакета вам поможет JBuilder. Нажмите комбинацию клавиш Ctrl+Alt+пробел, которые вызывают диалоговое окно, показанное на рис. 25.1. Наберите в поле Search For (Поиск) слово ArrayList. В поле Matching List (Список совпадений) появится имя искомого класса, как показано на рис. 25 2

В нижней части рис. 25.2 вы видите опцию, которая позволяет вставить полностью опреде­ленное имя класса непосредственно в код про­граммы:

Void

JButtonl_actionPerformed(ActionEvent е)

{

Java. utiɪ.ArrayList list;

}

рис. 25.2. автоматический поиск пакета, в состав которого входит класс arraylistОстальные опции помещают в начало текущего файла кода явный оператор им­портирования или оператор импортирования класса ArrayList. Ниже показан простой пример короткого имени класса и явного импортирования.

Import java. util. ArrayList;

Void jButtonl_actionPerformed(ActionEvent e)

1

ArrayList list;

1

А теперь посмотрим на импортирование пакета и короткое имя класса: import java. util.*;

Void jButtonl_actionPerformed (ActionEvent e)

{

Java. util. ArrayList list;

1

Понимание пакетов

Рассмотрим следующий пример, который содержит часто используемые опера­торы.

Import j ava. awt.*; import, java. awt. event.*; imporl javax. swing.*;

Несмотря на то что приведенные операторы import входят в JDK, тем не менее, оно импортируют пакеты, тип которых будет рассмотрен в этой главе. Первые два оператора импортируют пакеты, имена которых начинаются со слова "java”, Ука­зывающего на то, что пакеты входят в состав API-интерфейса Java. Слово "java" Го­ворит о многом. В частности, оно указывает, что пакет входит в состав JDK и, воз­можно, по умолчанию будет частью любой платформы Java.

И первый, и второй операторы содержат ссылки на пакет Awt. ,’awt,’ представляет собой аббревиатуру от Abstract ∖Mndow Toolkit (пакет инструментов для создания абстрактных окон) который используется для создания пользовательских интер­фейсов. Основные классы Java, например, Button, Font, CheckBox, Dialog, Label И Menu, Находятся именно в этом пакете.

Во втором операторе Import Содержится ссылка на пакет Event. Он содержит код, имеющий отношение к событиям, например, таким событиям, как ActionEvent, ActionListener, FocusEvent И MouseListener.

Третий оператор import импортирует пакет Javax. swing.*. Пакет Javaχ Содер­жит набор расширений для Java, разработанных компанией Sun, а пакет Swing Со­держит еще один набор элементов управления пользовательского интерфейса, в час­тности, кнопки, поля со списками и т. д. Имена классов пакета Swing Начинаются с буквы j: JButton, JListBox, JTree И т. д. Как правило, кнопки и другие элементы управления из пакета Swing Обладают расширенными возможностями по сравне­нию с аналогичными элементами управления пакета Java. awt.

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

Создание набора утилит CodeBox

Утилиты, которые будут созданы в настоящей главе, будут группироваться анало­гично процедурам в пакетах Java. awt, java. awt. event И Java. swing. В Частности, они будут храниться в пакете с именем Com. eivenwarβ.*. Например, их имена будут выглядеть следующим образом: Com. elvenware. codebox.* И Com. elvenware. comp.*. В Первом приведенном пакете содержатся простые утилиты, а во втором — компо­ненты.

Первое слово в имени пакета, "сот", Означает, что утилиты разработаны компа­нией (от англ, "company"), второе слово, "Elvenware", Представляет собой имя ком­пании автора книги, а третье слово, "codebox", Является собственно именем набора процедур.

Для использования набора процедур в приложении в его исходный код необхо­димо добавить оператор Import com. eivenware. codeboχ.*. Кроме того, при жела­нии в операторе Import Можно указать имя определенного класса, входящего в со­став пакета, например, Com. elvenware. codeboχ.StringBox. Такой синтаксис по­зволяет ссылаться на один класс из пакета Codeboχ.

Таким образом, остается только один вопрос — каким образом создать проект, использующий файлы утилит. Как выглядит структура проекта и как настроить сре­
ду JBuilder, чтобы она правильно воспринимала проекты такого рода? Ответы на эти вопросы будут приведены в следующем разделе, в котором будет рассмотрена про­грамма, демонстрирующая принципы, используемые при создании проекта со ссылками на внешние пакеты.

Создание сложных проектов

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

Для создания проекта, тип которого был описан в предыдущем разделе, все его файлы, как и ранее, должны размещаться в каталоге Src, Который используется в среде JBuilder по умолчанию. Но теперь каталог src будет содержать два подкатало­га: первый будет использоваться для хранения основного кода проекта, а второй — для хранения кода разрабатываемых утилит. На рис. 25.3 показано дерева каталога Src Так, как оно выглядит в приложении KDE Konquerer в операционной системе Linux Red Hat 7.2.

примечаниерис. 25.3. отображение структуры каталогов проекта stringtestol в диспетчере файлов kde konquererMβs∙Mβmhu0 Вы, ВОЗмОЖНОг обратили внимание, что некоторые каталоги на рис. 25.3 называются CVS. Они создаются системой управления версиями с открытым исходным кодом. Они не содержат кода Java и практически не имеют отношения к теме настоя­щей главы. Тем не менее, система CVS представляет собой очень полезную и достаточ­Но хорошо сделанную программу. Она будет рассматриваться в следующей главе.

Q-⅛t fikr∕hon∙βfcharlι∙⅛rr⅛ ɪɜavw jhh∞kCot*pb∙И

⅛βj ɪetaʃfitnnglj В ■ ι∏’ χ i

LpcSition EcKt √- -. ⅛. Bpok∏ωk* lɔols Settngx Jff tdow ЦSip

■’ ∣>⅞….. л.,. у ∙-. ..x∙.⅛m⅛∙..∙⅜⅞⅝Λ.-.∙z.√*.-^.-√j. a ДЛ… →.. — S⅛≥⅛,,X.∙⅛~~m⅛Z.√z ⅛-.~.- Я»⅛∙iv — …■>-….

E> t-ɔul o-[ιqIβ ⅛ιθBrejavaJbbool√CoπtplexPιvEJβ∕St-ingTβstOVstcrwi li R~…………….. — —⅛ * ~ !IiilIiiilHiiwriIinSiNi-IiiI-JjJ ∣⅛⅛i∣u1pH∣LT⅛⅛⅛⅛J

Ь~ t ^StringTaetOI i QCVS

’ I ffltj, bak

В Qjaeeae

; QCVS

B∙’⅛∞∏∣

: В — Qelvenwaie Qoodeboi

В Qdependency cache B — f⅛^tri ɪgtea

I _QCVS

I В_______

Cam OfiS

, Qcvs Ξ Qooni

, ∣ i, Qcvs

B’ Qelventvan

; Qcvs В Qoodebox

LQCVS

В Qstringtesl

TC

496

Основной исходный код приложения будет находиться в каталоге Stringτestθl∕src∕stringtest, А исходный код утилит — в каталоге StringTestOl∕src∕com∕elvenware∕codebox. Еще раз посмотрите на рис. 25.3, если вы не вполне понимаете, каким образом каталоги выглядят на жестком диске. Оператор Import В файле StringBox. java, Который расположен в каталоге Codebox, Выглядит следующим образом:

Package com. elvenware. codebox;

А оператор import в коде, находящемся в каталоге Stringtest (в каталоге основ­ного кода проекта), выглядит так:

Import com.ЕIvenware. codebox. StringBox;

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

В частности, помните, что стандартный проект JBuilder помещает каталог Src, Показанный на рис. 25.3, в каталог, предназначенный для размещения классов сре­ды JBuilder. Настроить путь к этому каталогу можно с помощью меню Project ∣ Project Properties среды JBuilder, установив нужный путь в разделе Source на странице Path.

Тем не менее, вы должны указать среде Jbuilder, каким образом создавать файл com. elvenware. codeboχ.StringBox. Java. Ничто не мешает вам создать его вруч­ную, но оказывается, JBuilder может сделать это автоматически, если только его пра­вильно об этом попросить. Посмотрите на рис. 25.4.

-4 createanewjamaciass
fill inthe fields below to set the package, name, base class, and other options for the
,class name: jstringbox,base class. jjavajang-object
ps ~∙
,й,p generate default constructor qrerrtde abstract methods

‘,Class Wizard

Java class which will be created.

рис. 25.4.
создание нового файла класса для нового пакета
public
(“ generatemainmethod ≠ 3⅞ $
7 generateheaderconiments

Classinformation Package:

ɪ.

cancel heip

Диалоговое окно, показанное на этом рисунке, можно открыть с помощью пунк­та меню File ∣ New Class. Затем в поле Package введите имя создаваемого пакета. В поле Class Name укажите имя создаваемого класса. После этого нажмите кнопку OK. JBuilder автоматически создаст все новые каталоги, которые являются частью пути StringTestOiSrcComElvenwareCodebox И автоматически создаст заго­товку ДЛЯ Файла StringBox. java.

Вот и все. Теперь вы можете просто добавить методы в файл StringBox. java, Операторы Import В файл Framel. java И проект готов. Нет ничего проще, если вы, конечно, знаете, что делать!

Изучение проекта StringTestOI

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

Исходный код утилит приведен в листинге 25.1, а код основной программы — в листинге 25.2. Код самого приложения не показан, поскольку он представляет со­бой всего лишь шаблон, который генерируется по умолчанию в любом проекте JBuilder данного типа, как было описано в главе 3.

* Gparam — строковое значение, первое слово которого извлекается.

*

* Greturn — первое слово, извлеченное из аргумента метода

* Если строка содержит всего одно слово, то возвращается вся строка.

* Если строка пустая или содержит Null, возвращается пустая

* Строка.

*/

Public static final String getFirstWord(String value)

{

∕ I Удалить пробелы

String CleanValue = value. trim();

∕∕ Определить индекс первого пробела

Int IndexOfFirstSpace = CleanValue. IndexOf(, * ) ;

// Если в строке всего одно слово, вернуть его If (IndexOfFirstSpace == 0)

Return CleanValue;

}

// Вернуть первое слово строки

Return CleanValue. substring(O, IndexOfFirstSpace);

}

/**

* Удаление первого слова строки. Если строка пустая, возвращается

* Пустая строка. Если строка содержит одно слово, вернуть это слово.

*

* Gparam — исходная строка

* Greturn — первое слово исходной строки

* Gsee getFirstWord()

*/

Pxιblic static final String StripFirstWord (String value)

<

∕∕ Удалить пробелы

String cleanstring = value. trim ();

∕∕ Определить индекс первого пробела

Int IndexOfFirstWhiteSpace ≈ temp. indexθf(’ ’) ;

// Если в строке всего одно слово, вернуть его If (IndexOfFirstWhiteSpace ≈ -1)

Return value;

// Определить длину строки

Int StringLength = temp. length();

11 Вернуть всю строку без первого слова Return temp. substring(IndexOfFirstWhiteSpace,

StringLength).trim();

)

∕*1t

* Изменение порядка символов строки на обратный

* Gparam — исходная строка

* Greturn — строка с обратным порядком символов */

Public static final String reverse(String value)

{

// Зарезервировать память для возвращаемой строки StringBuffer result = new StringBuffer(value); return result. reverse().toString();

}

)

Листинг 25.2. Исходный код основной программы проекта

Package Stringtest;

Import java. awt.*;

Import java. awt. event.*;

Import javax. swing.* ;

Import com. eɪvenware. codebox. StringBox;

Public class Main extends JFrame

{

JPanel ContentPane;

JMenuBar jMenuBarl = new JMenuBar();

JMenu jMenuFile = new JMenu () ;

JMenuItem jMenuFileExit = new JMenuItemO;

JMenu jMenuHelp = new JMenu () ;

JMenuItem JMenuHelpAbout = new JMenuItem () ;

JToolBar j ToolBar = new JToolBarO ;

JButton jButtonl = new JButton();

JButton jButton2 = new JButtonO ;

JButton jButton3 = new JButton();

ImageIcon imagel;

ImageIcon image2;

ImageIcon image3;

JLabel StatusBar = new JLabel();

BorderLayout borderLayoutl = new BorderLayout(); JMenu jMenul = new JMenu () ;

JMenuItem jMenuItemReverse = new JMenuItem() ; JMenuItem jMenuItemGetFirstWord = new JMenuItemO ; JMenuItem jMenuItemStripFirstWord = new JMenuItemO ; /**Соэдание фрейма*/

Public Main()

{

EnableEvents(AWTEvent. WINDOW_EVENT_MASK);

Try

{

JblnitO ;

}

Catch(Exception e)

{

E. printStackTrace();

}

/♦♦Инициализация компонент*/ Private void jblnit() throws Exception

Imagel = new ImageIcon(Stringtest. Main. class. gβtResource("openFile. gif")); image2 = new ImageIcon(stringtest. Main. class. CfetBesource("CloseFile. gif")); image3 = new ImageIcon(stringtest. Main. class. getResource("help. gif"));

ContentPane = (JPanel) this. getContentPane ();

ContentPane. SetLayout(borderLayoutl);

This. setSize(new Dimension(400, 300));

This. SetTitle("String Test");

StatusBar. setText(" ") ;

JMenuFile. setText("File”);

JMenuFileExit. setText("Exit");

MainListener listen = new MainListener(this);

JMenuFileExit. addActionListener(listen);

JMenuHelp. setText("Help");

JMenuHelpAbout. setText("About");

JMenuHelpAbout. SddActionListener(Iisten);

JButtonl. setlcon(Imagel);

JButtonl. SetToolTipText("Open File”);

JButton2.setlcon(image2);

JButton2.SetToolTipText("Close File"); jButton3.setlcon(image3); jButton3.SetToolTipText("Help"); jMenul. setText("Options"); jMenuItemReversθ.setText("Reverse");

JMenuItemReverse. SddActionListener(Iisten); JMenuItemGetFirstWord. setText("GetFirstWord"); JMenuItemGetFirstWord. SddActionListener(listen); JMenuItemStripFirstWord. setText("StripFirstWord"); JMenultemstripFirstWordtSddActionListener(Iisten); JToolBar. add (JButtonl);

JToolBar. add(jButton2) ;

JToolBar. add(JButton3);

JMenuFile. add (JMenuFileFxit) ;

JMenuHelp. add (JMenuHelpAbout);

JMenuBarl. add (JMenuFile);

JMenuBarl. add (JMenul);

JMenuBarl. add (JMenuHelp); this. SetJMenuBar(JMenuBarl);

ContentPane. add(JToolBar, BorderLayout. NORTH);

ContentPane. add(statusBar, BorderLayout. SOUTH);

JMenul. add (JMenuItemReverse);

JMenul. add(JMenuItemGetFirstWord);

JMenul. add (JMenuItemStripFirstWord);

>

/♦♦Выполнение действия File Fxit*/

Public void JMenuFileFxit-actionPerformed(ActionFvent e)

[

System. exit(O);

/♦♦Выполнение действия Help About*/

Public void JMenuHelpAbout_actionPerformed(ActionFvent e)

[

Main_AboutBox dig = new Main_AboutBox(this);

Dimension dlgSize = dlg. getPreferredSize();

Dimension frmSize = getSize();

Point Ioc = getLocation();

Dlg. SetLocation((frmSize. width — dlgSize. width) ∕ 2 + loc. x, (frmSize. height — dlgSize. height) ∕ 2 + loc. y);

Dig. setModal(true);

Dig. show();

,

/♦♦Перекрыт, чтобы можно было выйти при закрытии окна*/ Protected void processWindowEvent(WindowFvent e)

Super. ProcessWindowFvent(e);

If (e. getID() = WindowEvent. WINDOW-CLOSING)

{

JMenuFileFxit_actionPerformed(null);

)

Void jMenuItemReverse_actionPerformed(ActionFvent e)

String data = "Able was I ere I saw Elba";

∕∕ Пример изменения порядка символов строки на обратный If (e. getSource() == JMenuItemReverse)

{

Data = StringBox. reverse(data);

J

// Пример использования GetFiГStWord if (e. getSource() ≈ jMenuItemGetFirstWord)

{

Data ≈ StringBox. getFirstWord(data);

) *

∕∕ Пример использования StripFiretWord if (e. getSource() = jMenuItemStripFirstWord)

<

Data = StringBox. StripFirstWord(data);

I

JOptionPane. ShowMessageDialog(this, data);

}

)

∕** Для всех элементов меню в приложении я использую один и тот же

* Класс ActionListener. Более того, большинство из них обрабатываются

* Одним и тем же обработчиком в главной программе.

*/

Class MainListener implements ActionListener

<

Main adaptee;

MainListener(Main adaptee)

(

This. adaptee = adaptee;

)

Public void actionPerformed(ActionEvent e)

{

If (e. getSource() ≈ adaptee. jMenuFileExit)

(

Adaptee. JMenuFileExit_actionPerformed(e);

}

If (e. getSource() ≈ adaptee. jMenuHelpAbout)

{

Adaptee. jMenuHelpAbout_actionPerformed(e);

}

If (e. getSource() == adaptee. JMenuItemReverse)

{

Adaptee. jMenuItemReverse_actionPerformed(e);

}

If (e. getSource() ≈ adaptee. JMenuItemGetFirstWord)

(

Adaptee. jMenuItemReverse_actionPerformed(e);

¾ if (e. getSource() == adaptee. JMenuItemStripFirstWord)

{

Adaptee. JMenuItemReverse_actionPerformed(e);

}

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

примечаниеОбработка строк, показанная в пакете StringBox. java, часто выпол­няется с помощью регулярных выражений. Регулярные выражения представляют собой часть JDK версии 1.4, но в приведенном примере я их не использую, поскольку книга по­священа в основном JDK 1.3.1. Разработанные функции имеют небольшую длину, быстро Выполняются и удобны для понимания.

И здесь хотелось бы отметить, что JUnit Это технология, которую удобно ис­пользовать при тестировании кода такого типа, который содержится в файле StringBox. java. Технология JUnit будет подробно рассмотрена в главе 30.

В отношении исходного кода нашего проекта можно отметить два аспекта. Пер­вый связан с методом объявления объектов и методов в файле StringBox. java, А второй — C обработкой событий В файле Main. java.

При создании набора утилит для работы со строками одним из естественных пу­тей было бы создание потомка от класса string. В этом случае, например, для из­менения порядка символов в строке на обратный можно было бы воспользоваться синтаксисом следующего вида: MyString. reverse (). К сожалению, такой подход невозможен, поскольку класс string в Java объявлен как окончательный (final):

Public final class String implements java. io. Serializable, Comparable {

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

Вместо этого класс StringBox сам объявлен как окончательный, а все его методы объявлены статическими и окончательными. Во-первых, такой подход позволяет использовать достаточно простой синтаксис:

MyString ≈ StringBox. reverse(myString);

А во-вторых, Java будет использовать все методы класса StringBox как встроен­ные (inline). В частности, если класс объявлен окончательным, а его методы — окон­чательными и статическими, Java сгенерирует очень быстрый inline-код для каждого метода класса, inline-код вставляется непосредственно в код программы, таким об­разом исключая дополнительные затраты времени, связанные с вызовом метода. Тем не менее, с другой стороны, код программы будет занимать больше места, чем в случае использования вызовов методов. Другим словами, вы получаете скорость за счет увеличения объема. В большинстве случаев увеличение скорости работы про­граммы вполне себя оправдывает.

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

Метод изменения порядка символов в строке на обратный реализован следую­щим образом:

Public static final String reverse(String value)

{

∕∕ Зарезервировать память под возвращаемую строку StringBuffer result = new StringBuffer(value); return result. reverse().toString();

}

C другой стороны, его можно переписать и в таком виде:

Public static final String reverse(String value)

{

∕∕ Зарезервировать память для возвращаемой строки String result = new StringO ;

∕∕ Определить длину исходной строки int Ien = value. length () ;

11 ItLoxaxpobwsb символы из исходной строки по одному

∏∙a обратном порядке

For (int i = 1; i < (len + 1) ; i++)

{

Result += value. substring(Ien — i, (len — i) +1) ;

}

∕∕ Вернуть строку с обратным порядком символов return result;

}

В последнем примере переменная возвращаемого результата объявлена как string. Более эффективная версия того же метода показана ниже:

Public static final String reverse(String value)

<

∕∕ Зарезервировать память для возвращаемой строки StringBuffer result = new StringBuffer(256) ;

// Определить длину исходной строки int Ien = value. length () ;

// Копировать символы из исходной строки по одному

/ / в обратном порядке

For (int i = 1; i < (len + 1) ; i++)

{

Result, append (value, substring (len — i, (len — i) + 1) ) ;

}

∕∕ Вернуть строку с обратным порядком символов return result. toString() ;

}

Последняя версия использует оператор result. append, а не result +=. Класс string нельзя использовать при постоянном изменении размера строки, поскольку он неизменяем. Проблема состоит в том, что код, находящийся в цикле for, приве­дет к многократным вызовам конструктора класса string. При каждом изменении экземпляра класса string будет вызываться его конструктор. C другой стороны, класс StringBuffer создается только один раз и подходит для использования в данной ситуации.

примечание

∣∣∣e∣∣∣∣∣-e∣B∣B∣(∣-∣ ц случае применения операции конкатенации строк (т. е. +), компилятор реализует код с использованием класса StringBuffer. Более подробное описание класса StrxngBuffer содержится в Javadoc-файле, поставляемом компанией Sun для класса StringBuffer. В интегрированной среде разработки JBuiIder вы имеете воз­можность просмотреть содержимое этого файла путем открытия класса StringBuffer в редакторе кода и выбора закладки Doc среди закладок просмотра, расположенных в нижней части окна редактора. Кроме того, объявление переменной класса StringBuffer в среде JBuiIder можно найти или ввести, поместив курсор на слове и нажав клавишу F1, которая позволяет найти соответствующий Javadoc-файл. И, наконец, Javadoc-фай — лы для класса StringBuffer доступны на Web-сайте по следующему URL-адресу: Http:∕/java. sun. сот/J2se∕l.4.l∕docs∕aρi∕java∕lang∕StringBuffer. html.

События в программе StringTestOI

Я подробно рассматривал события в главах 14 и 15. Тем не менее, мы должны вер­нуться к ним еще раз, чтобы описать механизм событий, используемый в программе StringBoxOl.

В исходном коде файла Main, Java Ограничивается количество содержащихся в нем классов ActionListener. В программе реализован целый ряд пунктов меню. Вместо создания класса ActionListener Для каждого пункта меню все сообщения передаются одному, ХОТЯ И Достаточно ДЛИННОМУ Классу ActionListener:

Class MainListener implements ActionListener

{

Main adaptee;

MainListener(Main adaptee)

(

This. adaptee = adaptee;

1

Public void actionPerformed(ActionEvent e)

{

If (e. getSource() == adaptee. jMenuFileExit)

{

Adaptee. jMenuFileExit_actionPerformed(e);

1

If (e. getSource() = adaptee. jMenuHelpAbout)

{

Adaptee. jMenuHelpAbout_actionPerformed(e);

1

If (e. getSource() ~ adaptee. jMenuItemReverse)

{

Adaptee. jMenuItemReverse_actionPerformed(e);

1

If (e. getSource() ≈ adaptee. JMenuItemGetFirstWord)

{

Adaptee. jMenuItemReverse_actionPerformed(e);

1

If (e. getSource() == adaptee. JMenuItemStripFirstWord)

{

Adaptee.JMenuItemReverse_ActionPerformed(E);

}

Как видите, в коде производится сравнение с каждым пунктом меню, который может обращаться к услугам класса ActionListener, И вызывается метод, соответ­ствующий выбранному пункту:

// Следующая строка определяет, какой пункт меню вызвал // класс ActionListener

If (e. getSource() == adaptee. JMenuItemStripFirstWord)

{

// А Эта Строка Вызывает Обработчик Событий Adaptee. jMenuItemReverse_actionPerformed(е);

}

При создании класса ActionListener В методе jbinit создается всего один объект-слушатель, который затем присваивается каждому пункту меню:

MainListener listen = new MainListener(this);

JMenuFileExit. addActionListener(listen);

JMenuHelpAbout. addActionListener(listen); И Т.Д.

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

Более сложные пути

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

В предыдущем разделе вы научились добавлять в проект новый набор файлов. Файлы, которые были добавлены в пакет com. elvβnwarβ.codebox, не имели ничего общего с исходным пакетом, который назывался Stringtest. Это были два совер­шенно разных пакета.

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

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

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

В программе, которая будет рассмотрена в этом разделе, в простой Java-аплет до­бавляется класс Timer. Тем не менее, он будет находиться не в том же каталоге, что и основной файл приложения. Файл класса Timer будет размещен в подкаталоге ос­новного каталога проекта. Рассмотрим пример:

MyProjectSrcMyprojectMynewpackageMyNewPackage.Java

В этом примере пакет Mynewpackage Находится в подкаталоге каталога Myproject. Каталог Myproject Создается по умолчанию средой JBuilder. Подката­лог Mynewpackage Это тот каталог, который мы добавляем в проект. В качестве сравне­ния давайте рассмотрим структуру последней рассмотренной нами программы:

MyProjectSrcMyproject

MyProjectSrcComElvenwareCodebox

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

MyProjectZ src∕myproject∕

MyProject∕src∕com∕elvenware∕codebox∕

∕com∕eIvenwarЕ∕codebox

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

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

Исходный код проекта можно найти в листингах 25.3 и 25.4. В данном примере первый листинг содержит код главного приложения, а второй — код класса Timer. Исходный код этого проекта можно также найти в сопровождающих материалах, в каталоге Timeit

Листинг 25.3. Исходный код главного приложения

Package timeit;

Import java. awt.*;

Import java. awt. event.;

Import javax. swing.*;

Import timeit. elftimer. ElfTimer;

Import com. borland. jbcl. layout.*;

Public class Framel extends JFrame

{

JPanel ContentPane;

BorderLayout borderLayoutl = new BorderLayoutO ;

DefaultListModel IistModel = new DefaultListModelO;

JPanel jPanell ≈ new JPanel();

JButton jButtonLiStBox = new JButton();

JButton JButtonStandardOut = new JButton ();

JScrollPane jScrollPanel = new JScrollPane();

JList jListl = new JList() ;

ElfTimer timer = new ElfTimer ();

JTextField jTextFieldTime = new JTextField();

VerticalFlowLayout VerticalFlowLayoutl = new VerticalFlowLayoutO; /♦♦Создание фрейма*/ Public Framel()

{

EnableEvents(AWTEvent. WINDOW_EVENT_MASK);

Try

F

Jblnit() ;

}

Catch(Exception e)

{

E. PrintStackTrace O;

}

}

/♦♦Инициализация компонентов*/ Private void jblnit() throws Exception {

ContentPane = (JPanel) this. getContentPane();

ContentPane. setLayout(borderLayoutl); this. setSize(new Dimension(400, 300));

This. setTitle("Frame Title");

JButtonListBox. setText("List Box");

JButtonListBox. addActionListener(

New Framel_jButtonListBox_actionAdapter(this));

JButtonStandardOut-SetTextCStandard Out") ;

JButtonStandardOut. addActionListener(

New Framel_jButtonStandardOut_actionAdapter(this));

JListl. setModel(IistModel);

JPanell. setLayout(VerticalFlowLayoutl);

ContentPane-add(JPanell, BorderLayout. SOUTH);

JPanell-add(JTextFieldTime, null); j Panell. add(jButtonStandardθut, null);

JPanell. add(JButtonListBox, null);

ContentPane. add(j ScrollPanel, BorderLayout. CENTER);

JScrollPanel. getViewport().add(jListl, null);

}

/♦♦Перекрыт, чтобы можно было выйти при закрытии окна*/

Protected void processWindowEvent(WindowEvent e)

{

Super. processWindowEvent(e);

If (e. getID() == WindowEvent. WINDOW_CLOSING)

(

System. exit(O) ;

}

}

Void jButtonListBox_actionPerformed(ActionEvent e)

{

Timer. start () ;

StringBuffer S ≈ new StringBuffer(256); for(int i = O; i < 150; i++)

{

S. SetLength(O);

S. append("Sam: " + String. valueθf (i).) ;

IistModel. addElement(S);

)

Timer. stop();

JTextFieldTime. setText ("Elapsed time: ,, + timer. getTim⅝Str () ) ;

)

Void jButtonStandardOut_actionPerformed(ActionEvent e)

{

Timer. start();

StringBuffer S = new StringBuffer(256); for(int i = O; i < 15000; i++)

{

S. setbengtħ (O) ;

S. append("Sam: " + String. valueθf(i));

System. out. println(S);

}

Timer. stop();

JTextFieldTime. setText("Elapsed time: " + timer. getTimeStr()) ;

}

)

Class Framel_jButtonListBox_actionAdapter implements java. awt. event. ActionListener

{

Framel adaptee;

Framel_jButtonListBox_actionAdapter(Framel adaptee)

{

This. adaptee = adaptee;

)

Public void actionPerformed(ActionEvent e)

{

Adaptee. jButtonListBox_actionPerformed(e);

}

)

Class Framel_jButtonStandardOut_actionAdapter
implements java. awt. event. ActionListener

{

Framel adaptee;

Framel_jButtonStandardOut_actionAdapter(Framel adaptee)

(

This. adaptee = adaptee;

)

Public void actionPerformed(ActionEvent e)

{

Adaptee. jButtonStandardOut_actionPerformed(e),

)

Листинг 25.4. Класс EIfTimer позволяет определять продолжительность событий в Java — программах

Package time! t.elftimer;

∕**

* Title: Time It

* Description:

* Copyright: Copyright (c) 2001 by Charlie Calvert

* Company: Elvenware

* @author Charlie Calvert

* Gversion 1.O */

Public class ElfTimer

{

Private long StartTime; private long StopTime; private boolean running; public ElfTimer()

{

StartTime = -1;

}

Public long getTime()

{

Return StopTime — startTime;

}

Public String getTimeStr()

{

Return String. valueθf(getTime());

}

Public boolean getRunning()

{

Return running;

}

Public long getStartTime() f

Return startTime;

}

Public void start()

{

StartTime = System. CurrentTimeMillis(); running = true;

}

Public void stop()

{

StopTime ≈ System. CurrentTimeMillis(); running = false;

}

Когда я запустил эту программу, то получил достаточно неожиданные результа­ты. Программа определяет время, которое необходимо для записи строки 15 000 раз на стандартное устройство вывода и 15 000 раз в окно со списком. В принципе, я ожидал, что запись на стандартное устройство вывода должны занять меньше вре­мени. Более того, я предполагал, что в Java могут возникнуть проблемы при необхо­димости записи в окно со списком 15 000 строк. Выходит так, что в обоих случаях я ошибался. Как видите, все предположения необходимо проверять, а не полагаться исключительно на интуицию.

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

Void jButtonListBox_actionPerformed(ActionEvent Е)

{

Timer. start();

StringBuffer S = new StringBuffer(256) ;

For(int i = O; i < 150; i++)

{

S. SetLength(0) ;

S. append("Строка: ” + String. valueθf(i));

IistModel. addElement(S);

}

Timer. stop();

JTextFieldTime. SetText("Затраченное время: n + timer. getTimeStr());

)

Void jButtonStandardOut_actionPerformed(ActionEvent Е)

{

Timer. start();

StringBuffer S = new StringBuffer(256); for(int i = O; i < 15000; i++)

. {

S. SetLength(O);

S. append("Строка: " + String. valueθf(i));

System. out. printin(S);

)

Timer. stop();

JTextFieldTime. SetText("Затраченное время: " + timer. getTimeStr());

)

Как видите, оба метода основаны на выполнении простых циклов. Один цикл ге­нерирует строки для стандартного устройства вывода, а второй — для окна списка.

Запись строки в окно списка занимает от одной четвертой до одной восьмой от времени, необходимого для записи строки в стандартное устройство вывода. Более того, запись 15 000 строк в окно списка занимает около четверти секунды. При оп­ределении времени выполнения записи я пользовался машиной с тактовой часто­той 800 МГц, работающей под управлением операционной системы Red Hat Linux 7.2, при этом объем ОЗУ составлял 448 Мб (объем ОЗУ определялся с помощью proc∕meminfo).

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

Пути классо в приложении Timer

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

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

511

Класс Timer расположен в подкаталоге основного каталога программы. Это пока­зано на рис. 25.5, из которого видно, что файл EifTimer. java находится в каталоге ∕src∕time! t/eɪftimer. Основные файлы проекта, как и ожидалось, хранятся в ка­талоге ∕src∕timeit∕.

Для указанного размещения файлов в меню JBuilder выберите File ∣ New class. При вводе имени и пакета класса укажите имя нового пакета с использовани­ем точечной нотации. Соответствующий пример показан на рис. 25.6. Для этого введите имя текущего пакета, в котором находятся ваши файлы, точку, а затем имя нового пакета, который необходимо создать. В приведенном примере текущим па­кетом является timeit, а новый пакет получил название elftimer. После выполне­ния этих операций автоматически будет создан новый каталог и в него будет добав­лен файл нового класса. Приведенный пример аналогичен примеру из предыдущего раздела, тем не менее, в нем присутствует новая логика. В конечном счете, вы буде­те использовать оба описанных метода, в зависимости от конкретных обстоятельств.

На этом этапе все, что вам нужно сделать — добавить в начале кода пакета соот­ветствующий оператор import и вы получите доступ к классам, находящимся в па­кете elftimer. Обратите внимание на оператор package в начале файла ElfTimer. java и оператор import в главной программе.

Package timeit. elftimer; //Из файла ElfTimer. java

Import timeit. elftimer. ElfTimer; // Из Файла Framel. java

Если код организован подобным образом, вам не нужно изменять пути классов или выполнять в среде JBuilder какие-то специальные операции. Все что от вас тре­буется — ввести код и щелкнуть на кнопке компиляции. Все остальное будет сдела­но автоматически.

Пути, находящиеся вне проекта

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

∙⅛ file ι∕lιome∕ccalvert∕src∕srcjava.'jbbook,'connμlex hn ч - tsf∏meXL=JBJjJ

LOcatjxn ξdit yiew go g□oknarks I∞ls Settings bɪndou Help

lɪ <⅛ ⅛ й $ ɔ ⅜;,≈4∙∣a⅛ s.,¾)nplexprojec⅛s7τ iwslt/src/tlnelt/elftimer у ∣,a-t ⅛^ -- --^ - ≥×
1 b⅛math testol b ⅛stringtestol
,b f⅛stringtesto2 b s^tineit,cvs,о
elf tirer .,kι⅛a
рис. 25.5. в диспетчере файлов kde видно, что файл elftimerjava находится в каталоге ∕srс/timeit∕elftimer
b ⅛sΓc j∙∙⅛cvs в stineit i∙∙⅜cvs в_
'c'i⅞cvs а
ixl
,jlst

Ве этих примеров для вас не составит особого труда изучить метод совместного ис­пользования кода несколькими приложениями внутри одного проекта. Но что де­лать, если вам необходимо использовать один и тот же код в нескольких проектах?

Рассмотрим окно, показанное на рис. 25.7. Как видите, проекты StringTestOl, stringTestO2, Timeit и τimeit02 находятся на том же уровне, что и каталог src. В каталоге src содержатся пакеты elvenware. codebox И timeit. elf timer. В ЭТОМ разделе я покажу, как использовать пакеты утилит из каталога src в программе

TimeItO2.

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

Рис. 25.7. Структура каталогов для набора проектов, которые совместно используют утилиты из каталога Src

Если вы внимательно читали предыдущий раздел, то, возможно, знаете, как дос­тичь поставленных перед нами целей. На рис. 25.8 вы видите диалоговое окно Project Properties в версии JBuilder для операционной системы Linux, открытое на странице Paths. Как видите, в разделе Source страницы присутствуют два элемента. Первая строка добавляется по умолчанию и указывает на каталог Src Проекта. Тем не менее, вторая строка указывает на каталог Src, Который находится на том же уровне, ЧТО И Корневые каталоги проектов StringTestOl, StringTestO2 И TimeIt. Эта вторая строка была добавлена в результате щелчка на кнопке Add и выбора соот­ветствующего каталога средствами JBuilder.

Исходный код проекта τimeit02 можно найти в листинге 25.5. Он содержит ссылки на пакет timeit. elf timer. Эта версия пакета не является частью проекта Timeit, а содержится в каталоге src, находящемся на том же уровне, что и проекты Timeit и τimeit02. Если вы не представляете себе структуру каталогов проектов, посмотрите еще раз на рис. 25.7.

Диалоговое окно Project Properties позволяет указать среде Jbuilder, где конкрет­но искать исходный код проекта. Оно выполняет ту же роль, что и ключ -Sourcepath, Передаваемый утилите командной строки Javac.

Внося единственное изменение в диалоговом окне Project Properties, вы имеете возможность использовать один и тот же код в нескольких проектах. Таким обра­зом, вы можете создать один специализированный проект для тестирования кода и использовать его в нескольких других проектах (Описание процесса развертывания проектов такого рода будет приведено в последующих главах. Пока что мы должны сконцентрировать свое внимание на создании и отладке проектов.)

Давайте коротко рассмотрим исходный код проекта τimeit02. Как я уже гово­рил, я не собираюсь подробно останавливаться на том, как работает проект или как его создать в среде JBuilder. Вы должны уделить основное внимание на разбор того, каким образом среда JBuilder работает с путями и пакетами.

рис. 25 л. диалоговое окно project properties может использоваться для организации доступа к коду, внешнему по отношению к проекту

Листинг 25.5. Исходный код, который сравнивает скорость работы и возможности компонентов JListBox и JTree

Package timeit02;

Import java. awt.*;

Import java. awt. event.*;

Import javax. swing.*;

Import javax. swing. tree. TreePath;

Import javax. swing. tree. DefaultMutableTreeNode;

Import timeit. elftimer. ElfTimer;

Import com. borland. jbcl. layout.*;

∕**

* Title: TimeIt02

* Description:

* Copyright: Copyright (c) 2001 by Charlie Calvert

* Company: Elvenware

* Sauthor Charlie Calvert

* Sversion 1.0 */

Public class Framel extends JFrame

{

JPanel ContentPane;

BorderLayout borderLayoutl = new BorderLayout();

JPanel jPanell = new JPanel();

JPanel jPanel2 = new JPanel();

DefaultMutableTreeNode rootNode = new DefaultMutableTreeNodeCMy Root Node"); DefaultListModel model = new DefaultListModelO;

JScrollPane jScrollPanel = new JScrollPane();

JTree jTreel = new JTree(rootNode);

ElfTimer timer = new ElfTimer ();

JScrollPane jScrollPane2 = new JScrollPane();

GridLayout gridLayoutl = new GridLayout();

JList JListl = new JList() ;

GridLayout gridLayout2 =■ new GridLayoutO;

JPanel jPanel3 = new JPanel();

JLabel jLabell = new JLabel O;

JTextField JTextFieldl = new JTextFieldO ;

BorderLayout borderLayout2 = new BorderLayoutO;

JPanel jPanel4 ~ new JPanel();

JButton jButtonAddToTree = new JButton ();

JButton jButtonCount = new JButton ();

JButton jButtonClearAll = new JButton();

JButton JButtonAddToListBox = new JButton();

GridLayout gridLayout3 = new GridLayoutO ;

/♦♦Создание Фрейма*/ Public Framel()

(

EnableEvents(AWTEvent. WINDOW_EVENT_MASK);

Try

<

Jblnit() ;

}

Catch(Exception e)

e.printstacktrace();

{

}

/**Инициализация Компонентов*/

Private void jbɪnit() throws Exception

<

ContentPane = (JPanel) this.9etC0ntentPane(); ContentPane. SetLayout(borderLayoutl); this. setSize(new Dimension(611, 413));

This. SetTitle("Frame Title");

JPanell. SetLayout(gridLayoutl); grIdLayoutl. setColumns(2);

JListl. setModel(model); j Panel2.setLayout(gridLayout2); gridLayout2.setRows(2); gridLayout2.setColumns(1);

JPanel2.SetPreferredSize(new Dimension(725, 75) ) ;

JLabell. setText("Elapsed Time");

JPanel3.SetLayout(borderLayout2);

JButtonAddToTree. setText("Add to Tree"); JButtonCount. setText("Count");

JButtonClearAll. setText("ClearAll"); JButtonAddToListBox-SetTextCAdd to List Box"); MyAdapter listener = new MyAdapter(this); JButtonClearAll. addActionListener (listener) ; jButtonAddToTree. addActionListener(listener); JButtonAddToListBox-SddActionListener(listener); JButtonCount. SddActionListener(listener); jPanel4.SetLayout(gridLayout3);

ContentPane. add(j Panell, BorderLayout. CENTER);

JPanell. add(jScrollPane2, null);

JScrollPane2.getViewport().add(jListl, null);

JPanell. add(jScrollPanel, null);

JScrollPanel. getViewport().add(jTreel, null);

ContentPane. add(j Panel2, BorderLayout. NORTH);

JPanel2.add(jPanel3, null);

JPanel3.add(JTextFieldl, BorderLayout-CENTER);

J Panel3.add(j Labell, BorderLayout. WEST);

JPanel2.add(jPanel4, null);

J Panel4.add(j ButtonAddToListBox, null);

J Panel4.add(JButtonAddToTree, null);

J Panel4.add(JButtonCount, null);

J Panel4.add(jButtonClearAll, null);

}

∕*«Перекрыт, Чтобы Можно Было Выйти При Закрытии Окна*/ Protected void ProcessWindowEvent(WindowEvent Е)

{

Super. ProcessWindowEvent(е);

If (e. getID() == WindowEvent. WINDOW_CLOSING)

(

System. exit(О);

)

)

Void ClearTree()

{

Model. clear();

RootNode. removeAHChildren () ; jTree1.updateUI();

Void jButtonAddToTree actionPerformed(ActionEvent e)

Timer. start();

For (int i = O; i < 5000; i++)

{

DefaultMutableTreeNode node =

New DefaultMutableTreeNodeCAaHHue: n + String. valueθf(i)); RootNode. add(node);

)

JTreel. expandPath(new TreePath(rootNode));

JTreel. updateDI(); timer. stop();

JTextFieldl. setText(timer. getTimeStr());

}

Void jButtonAddToListBox_actionPerformed(ActionEvent e)

(

Timer. start() ;

For (int i = O; i < 5000; i++)

{

Model. addElement ("Data: ,, + String. valueθf (i)) ;

)

Timer. stop ();

JTextFieldl. setText(timer. getTimeStr());

}

Void jButtonClearAll_actionPerformed(ActionEvent e)

{

ClearTree () ;

}

Void j ButtonCoun t_actionPerf ormed (ActionEvent e)

{

String S « "ListBox: " + String. valueθf (model. getSize ()) ;

S += " Tree: ,, + String. valueθf(rootNode. getChildCount ()) ; JOptionPane. ShowMessageDialog(this, S);

)

}

Class MyAdapter implements java. awt. event. ActionListener

(

Framel adaptee;

MyAdapter(Framel adaptee)

(

This. adaptee = adaptee;

}

Public void actionPerformed(ActionEvent e)

(

If (e. getSource() = adaptee. jButtonAddToTree)

{

Adaptee. jButtonAddToTree_actionPerformed(e);

)

Else if (e. getSource() == adaptee. jButtonAddToListBox)

{

Adaptee. jButtonAddToListBox_actionPerformed(e);

)

Else if (e. getSource() = adaptee. jButtonClearAll)

(

Adaptee. jButtonClearAll_actionPerformed(e);

)

Else if (e. getSource() ≈ adaptee. jButtonCount)

{

Adaptee. jButtonCount_actionPerformed(e);

примечаниеПрограмма τimeit02 мало чем отличается от программы Timeitoi. Вместо срав­нения стандартного устройства вывода с окном списка она сравнивает с окнами списков компонент JTrees. И сейчас вы обнаружите, что окна списков работают быстрее компонентов JTrees, Что в данном случае не является большим сюрпри­зом. Полученные мною результаты показали, что при работе с небольшим количе­ством элементов, скажем, менее 10 ООО, разница весьма незначительна. Тем не ме­нее, при помещении в окно списка и дерево 50 000 или 100 000 элементов заполне­ние дерева JTrees Занимает до двух секунд, в то время как окну со списком требуется менее секунды.

Вы только вообразите себе 100 000 элементов в одном окне списка! Это исключительно мощное средство JDK.

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

Один из наиболее сложных аспектов, которые встречаются среди со­провождающих книгу материалов, состоит в том, что такие файлы, как com. elvenware. codebox. StringBox. java, находятся сразу в нескольких различных каталогах. Вы, например, обнаружите экземпляры программы StringTestOl в подката­логе src каталога ComplexProjects и корневом каталоге всех рассмотренных в книге файлов исходного кода. Это последние версии файлов от Elvenware, которые дополняют и содержат последние версии кодов. Остальные каталоги предназначены всего лишь для демонстрации тем, рассматриваемых в этой и следующей главах. Последние вер­сии всех файлов можно найти на Web-сайтах издательства русскоязычной редакции Этой книги, а также на моем сайте Http://www. elvenware. com.

рис. 25.9. при реализации пользовательского интерфейса для проекта timeito2 было задействовано множество панелей jpanel и диспетчеров компоновки

Создание классов в каталогах вне текущего проекта

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

На рис. 25.10 обратите внимание, ЧТО каталог G:\SrcJava\jbbook\CoiDplexProjects ∖src содержится В Списке ДО каталога G:\SrcJava\jbbook\ComplexProjects ∖τimeit02∖src. Более того, он отмечен как каталог по умолчанию. Как результат, при создании нового класса он будет помещен в каталог compiexProjects∖src, а не В TimeItO2Src.

При необходимости перемещения файлов можно воспользоваться средствами корректировки связей или опцией переименования, доступ к которым можно полу­чить, выполнив щелчок правой кнопкой мыши на файлах в панели проекта. Автор книги обнаружил, что средства корректировки связей не учитывают установки, которые были ранее заданы на странице Project Properties ∣ Paths, но, скорее всего, это просто ошибка, которая, по идее, должны быть исправлена в будущих версиях JBuilder.

Резюме

В этой главе вы узнали много новых сведений о работе с проектами, которые содер­жат относительно сложные пути классов. По необходимости все рассмотренные в главе примеры были достаточно простыми. Тем не менее, при разработке серьезных Java — приложений проекты могут содержать десятки, а зачастую и сотни файлов. Управление всеми этими файлами является чрезвычайно сложной задачей. Средства, которые были рассмотрены в этой главе, помогут вам в работе со сложными проектами за счет их раз­биения на несколько пакетов. Как всегда, используется правило "разделяй и властвуй". Один из важных вопросов, которого я не коснулся в главе, — это упаковка каталогов файлов исходного кода, например, для проекта τime02, В JAR-файлы. Этот вопрос бу­дет подробно рассмотрен в главе, посвященной развертыванию, и в нескольких других главах книги, в частности, представленных в частях VII и VIII.

r^' project properties xРис. 25.10. Использование установок по умолчанию на странице Project Properties Paths Для указания, где должны размещаться вновь создаваемые файлы исходного кода

марджи и чарли калверт

Глава 26

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *