Microsoft Visual J++. Создание приложений и аплетов на языке Java
Новый язык программирования
Почему вам нужно изучать новый язык программирования Java?
Если ответить на этот вопрос кратко, то потому, что он специально ориентирован на самые передовые технологии, связанные с сетью Internet. Растущая популярность Internet и, в особенности, серверов WWW, создает для программистов новые возможности для реализации своих способностей. Затратив всего пару-тройку недель на чтение нашей книги, вы сможете проникнуться идеологией Java и, как мы надеемся, сумеете по достоинству оценить возможности этого языка программирования.
В этой главе мы расскажем об основных особенностях Java и библиотеках классов, которые поставляются в составе систем разработки приложений. После этого вы поймете, почему этот язык программирования выглядит весьма привлекательно именно для реализации сетевых проектов в Internet и в корпоративных сетях Intranet.
Первое приложение и первый аплет
Не стремясь быть оригинальными, начнем программирование на Java с составления простейшей программы, которая выводит текстовую строку “Hello, Java!”. Для этого вам сначала нужно установить среду разработки Microsoft Visual J++, запустив программу setup.exe, расположенную в корневом каталоге дистрибутивного компакт-диска.
Процесс установки не вызывает затруднений. Вы просто должны следовать инструкциям, появляющимся на экране. Заметим только, что если у вас уже была установлена система Microsoft Visual C++, то имеет смысл для установки Microsoft Visual J++ использовать тот же каталог, что был использован для установки Microsoft Visual C++. При этом формируется единая среда разработки приложений на языках программирвоания C++ и Java, что очень удобно.
Рисование в окне аплета
В предыдущей главе мы привели простейший пример аплета, который выполняет рисование текстовой строки в своем окне. Теперь мы более подробно расскажем вам о том, что и как может рисовать аплет.
Обработка событий
От аплетов Java было бы немного толку, если бы они не умели обрабатывать информацию, поступающую от мыши и клавиатуры. К счастью, такая обработка предусмотрена и она выполняется достаточно просто.
Когда пользователь выполняет операции с мышью или клавиатурой в окне аплета, возникают события, которые передаются соответствующим методам класса Applet. Переопределяя эти методы, вы можете организовать обработку событий, возникающих от мыши или клавиатуры.
Если вы создавали приложения для операционной системы Microsoft Windows, здесь для вас нет ничего нового - вспомните, как вы обрабатывали сообщение WM_LBUTTONDOWN или WM_CHAR. Когда пользователь выполнял действие с мышью или клавиатурой в окне приложения, функция этого окна получала соответствующее сообщение. Методы класса Applet, обрабатывающие события от мыши и клавиатуры, являются аналогами обработчиков указанных сообщений.
Заметим, что аплеты имеют дело только с левой клавишей мыши. В текущей версии Java вы не можете никаким образом задействовать в аплете правую или среднюю клавишу мыши.
Компоненты в окне аплета
Практически каждое приложение Windows, за исключением самых простейших, имеет такие органы управления, как меню, кнопки, поля редактирования текстовой информации, переключатели с независимой и зависимой фиксацией и списки. Кроме того, приложение Windows может создавать диалоговые панели, содержащие перечисленные выше и другие органы управления.
В окне аплета вы также можете разместить некоторые из перечисленных выше органов управления, а именно:
кнопки;
переключатели с независимой фиксацией;
переключатели с зависимой фиксацией;
статические текстовые поля;
однострочные и многострочные поля редактирования текста;
списки;
полосы просмотра
Самый большой и едва ли приятный сюрприз для вас это то, что при размещении перечисленных органов управления в окне аплета вы не можете задать для них точные координаты и размеры. Размещением занимается система управления внешним видом Layout Manager, которая располагает органы управления по-своему. Вы, однако, можете задавать несколько режимов размещения (последовательное, в ячейках таблицы и так далее), но не координаты или размеры. Это сделано для обеспечения независимости приложений Java от платформ, на которых они выполняются.
Органы управления создаются как объекты классов, порожденных от класса Component (рис. 5.1). Поэтому в дальнейшем мы будем называть органы управления компонентами.
Рис. 5.1. Взаимосвязь классов органов управления в приложениях Java
Класс Button позволяет создавать стандартные кнопки. Если вам нужна нестандартная кнопка (например, графическая кнопка), вы можете создать ее на базе класса Canvas.
Для создания переключателей с независимой или зависимой фиксацией предназначен класс CheckBox.
С помощью класса Label вы можете создавать в окне аплета текстовые строки, например, надписи для других компонент. Эти строки не редактируются пользователем.
Класс List, как нетрудно догадаться из названия, предназначен для создания списков.
С помощью класса Scrollbar вы можете создавать полосы просмотра, которые используются, в частности, могострочными полями редактирования текста.
Класс TextComponent служит базовым для двух других классов - TextField и TextArea. Первый из них предназначен для создания однострочных редакторов текста, второй - для создания многострочных редкаторов текста.
Для того чтобы понять, как компоненты размещаются на поверхности окна аплета системой Layout Manager, рассмотрим другую взаимосвязь классов Java, показанную на рис. 5.2.
Рис. 5.2. Компоненты и контейнеры
Класс Component служит в качестве базового класса для класса Container. Объекты этого класса, которые мы будем называть контейнерами, могут содержать объекты классов Component и Container. Таким образом, внутри контейнеров могут находиться компоненты и другие контейнеры.
Класс Applet, так же как и другие классы, произведенные от класса Container, является контейнером. Это означает, что аплет может содержать в себе компоненты (такие как органы управления) и контейнеры.
Заметим, что класс Applet наследуется от класса Container через класс Panel, в котором определены методы системы Layout Manager. Настраивая соответствующим образом Layout Manager, мы можем менять стратегию размещения компонент внутри окна аплета.
В окне аплета вы можете создать несколько объектов класса Panel (панелей), разделяющих окно на части. Для каждой такой панели можно установить свою стратегию размещения компонент и контейнеров, что позволяет выполнять достаточно сложную компоновку в окне аплета.
Теперь после такого краткого введения в контейнеры и компоненты мы перейдем к описанию методов создания отдельных компонент.
Настройка системы Layout Manager
В предыдущей главе мы рассказали вам о том, как создавать компоненты и размещать их в контейнере. Однако предложенный способ размещения компонент в окне контейнера едва ли можно назвать удобным, так как заранее трудно предугадать, на каком месте окажется тот или иной орган управления.
К счастью, имеются способы, позволяющие контролировать размещение отдельных компонент в окне контейнера. И хотя эти способы не позволяют задавать конкретные координаты и размеры органов управления, использовнные схемы размещения компонент будут правильно работать на любой аппаратной платформе (не забывайте, что Java создавалась как средство разработки приложений, способных выполняться на любой платформе).
В чем трудность создания пользовательского интерфейса для мультиплатформных систем?
В том, что разработчик приложения никогда не знает характеристики устройства отображения, установленные у пользователя. Он, в частности, не может заранее знать разрешение монитора, размер системного шрифта и другие характеристики, необходимые для компоновки диалоговых панелей в терминах абсолютных координат.
Средства пользовательского интерфейса AWT способны динамически измнять размеры компонент, подгоняя их “по месту” в системе пользователя. В результате значительно повышается вероятность того что внешний вид диалоговой панели, в каком она предстанет перед пользователем, будет похож на то, что ожидал разработчик.
Как мы уже говорили в начале предыдущей главы, расположением компонент внутри окна контейнера (например, внутри окна аплета) управляет система Layout Manager. Способ, которым она это делает, весьма непривычен для тех, кто создавал приложения Windows. Выбор этого способа обоснован необходимостью обеспечения совместимости с различными компьютерными платформами.
Работа с панелями
Панели, создаваемые на базе класса Panel, являются мощным средством организации диалогового интерфейса. Так как класс Panel произошел от класса Container, панель может содержать компоненты и другие панели. Для каждой панели можно определить режим размещения компонент, что позволяет создавать достаточно сложный пользовательский интерфейс.
В окне аплета вы можете создать несколько панелей, разделяющих его на части. В свою очередь, пространство, занимаемое панелями, также может быть разделено с использованием одного из описанных выше режимов размещения (рис. 7.1).
Рис. 7.1. Размещение нескольких панелей в окне аплета
Отдельные панели могут содержать в себе такие компоненты, как кнопки, переключатели, списки, текстовые поля и так далее.
Окна и диалоговые панели
До сих пор мы рисовали только в окне аплета или в окнах панелей, расположенных внутри окна аплета. Однако есть и другая возможность - приложения Java, полноценные и аплеты, могут создавать обычные перекрывающиеся окна, такие, например, как окно навигатора. Эти окна могут иметь меню (в отличие от окон аплетов). Пользователь может изменять размер таких окон при помощи мыши, перемещая рамку окна.
В составе библиотеки классов AWT имеется несколько классов, предназначенных для работы с окнами. Это класс Window, который произошел от класса Container, и его дочерние классы - Frame, Dialog и FileDialog (рис. 8.1).
Рис. 8.1. Иерархия классов, предназначенных для создания окон
Окно, созданное на базе класса Frame, больше всего похоже на главное окно обычного приложения Windows. Оно может иметь главное меню, для него можно устанавливать форму курсора. Внутри такого окна можно рисовать. Так как окно класса Frame (так же как и другие окна AWT) произошли от класса Container, вы можете добавлять в них различные компоненты и панели, как мы это делали с окнами аплетов и панелей.
На базе класса Dialog создаются окна диалоговых панелей, очень похожих на обычные диалоговые панели Windows. Такие панели не могут иметь меню и обычно предназначены для запроса какой-либо информации у пользователя.
Класс FileDialog предназначен для создания диалоговых панелей, с помощью которых можно выбирать файлы на локальных дисках компьютера. Так как аплеты не могут работать с файлами, в этой книге мы не будем рассматривать класс FileDialog. Информацию о том, как работать с файлами в приложениях Java, а также сведения об этом классе мы планируем включить в следующий том “Библиотеки системного программиста”, посвященный системе разработки Microsoft Visual J++.
Что же касается класса Window, то непосредственно этот класс редко применяется для создания окон, так как классы Frame, Dialog и FileDialog более удобны и обеспечивают все необходимые возможности.
Аннотация
Книга представляет собой первую часть практического пособия по созданию автономных приложений и аплетов, работающих под управлением навигаторов WWW, на языке программирования Java.
Описаны основные отличия языка программирования Java от С++, среда выполнения приложений Java, приемы работы в интегрированной среде Microsoft Visual J++, основные библиотеки классов Java и методики их использования с иллюстрацией на примерах конкретных приложений. Читатель научится создавать сложные интерактивные аплеты для страниц серверов WWW.
Эта книга будет полезна всем, кто начинает самостоятельно осваивать новый язык программирования Java, и может быть использована в качестве учебного пособия для студентов учебных заведений.
Базовые типы данных
В языке Java определено восемь базовых типов данных, перечисленных ниже:
Тип данных
Размер занимаемой области памяти
Значение по умолчанию
boolean
8
false
byte
8
0
char
16
‘x0’
short
16
0
int
32
0
long
64
0
float
32
0.0F
double
64
0.0D
Для каждого базового типа данных отводится конкретный размер памяти, который, как мы говорили в предыдущем разделе, не зависит от платформы, на которой выполняется приложение Java. Фактический размеры памяти, отведенные для хранения переменной, могут отличаться от приведенных выше, например, для хранения переменной типа short может быть зарезервировано слово размером 32 бита. Однако язык Java сделан таким образом, что это никак не повлияет на мобильность приложения. Забегая вперед, скажем, что в языке Java нет указателей, поэтому вы не можете адресоваться к элементам массива чисел по относительному смещению этих элементов в оперативной памяти. Следовательно, точный размер элемента в данном случае не играет никакой роли.
Все базовые типы данных по умолчанию инициализируются, поэтому программисту не нужно об этом беспокоиться. Вы можете также инициализировать переменные базовых типов в программе или при их определении, как это показано ниже:
int nCounter = 0;
int i;
i = 8;
Переменные типа boolean могут находиться только в двух состояниях - true и false, причем эти состояния никаким образом нельзя соотнести с целыми значениями. Вы не можете, как это было в языке С, выполнить преобразование типа boolean, например, к типу int - компилятор выдаст сообщение об ошибке.
Переменная типа byte занимает восемь бит памяти и про нее больше нечего сказать.
Что же касается типа char, то он используется для хранения символов в кодировке Unicode. Эта кодировка позволяет хранить национальные наборы символов, что очень удобно для интернациональных приложений, предназначенных для работы в Internet.
Переменные типа byte, short, int и long являются знаковыми. В языке Java нет беззнаковых переменных, как это было в языке С.
Приложение Java может оперировать числами в формате с плавающей точкой, определенным в спецификации IEEE 754. Тип float позволяет хранить числа с одинарной точностью, а формат double - с двойной.
Переменные базовых типов могут передаваться функциям в качестве параметров только по значению, но не по ссылке. Поэтому следующий фрагмент кода работать не будет:
int x;
void ChangeX(int x)
{
x = 5;
}
. . .
x = 0;
ChangeX(x);
После вызова функции ChangeX содержимое переменной x останется равным нулю.
Проблему можно решить, если вместо базовых переменных использовать объекты встроенных классов, соответствующие базовым переменным. О встроенных классах вы узнаете из следующего раздела.
Библиотека классов java.applet
Как нетрудно догадаться из названия, библиотека классов java.applet инкапсулирует поведение аплетов Java. Когда вы будете создавать свои аплеты, вам будет нужен класс Applet, расположенный в этой библиотеке классов. Дополнительно в библиотеке классов java.applet определены интерфейсы для подключения аплетов к содержащим их документам и классы для проигрывания аудиофрагментов.
Библиотека классов java.awt.image
В среде любой операционной системы работа с графическими изображениями является достаточно сложной задачей. В 14 томе “Библиотеки системного программиста”, который называется “Графический интерфейс GDI в MS Windows” мы детально рассмотрели вопросы, связанные с рисованием графики и обработкой графических файлов в среде операционной системы Microsoft Windows. Если вы будете рисовать графические изображения в среде IBM OS/2 или X-Windows, вам, очевидно, придется использовать другие методики и другой программный интерфейс. Большую сложность также вызывает разбор заголовков графических файлов, так как они могут иметь различный формат и иногда содержат неправильную или противоречивую информацию.
Когда вы программируете на Java, рисование и обработка графических изображений выполняется намного проще, так как вам доступна специально предназначенная для этого библиотека классов java.awt.image. Помимо широкого разнообразия и удобства определенных в ней классов и методов, отметим способность этой библиотеки работать с графическими изображениями в формате GIF. Этот формат широко используется в Internet, так как он позволяет сжимать файлы графических изображений во много раз без потери качества за счет устранения избыточности.
Библиотека классов java.awt.peer
Библиотека классов java.awt.peer служит для подключения компонент AWT (например, кнопок, списков, полей редактирования текстовой информации, переключателей и так далее) к реализациям, зависящим от платформы, в процессе создания этих компонент.
Библиотека классов java.awt
Для создания пользовательского интерфейса аплеты Java могут и должны использовать библиотеку классов java.awt. AWT - это сокращение от Abstract Window Toolkit (инструментарий для работы с абстрактными окнами).
Классы, входящие в состав библиотеки java.awt, предоставляют возможность создания пользовательского интерфейса способом, не зависящим от платформы, на которой выполняется аплет Java. Вы можете создавать обычные окна и диалоговые панели, кнопки, переключатели, списки, меню, полосы просмотра, однострочные и многострочные поля для ввода текстовой информации.
Библиотека классов java.io
В библиотеке классов java.io собраны классы, имеющие отношение к вводу и выводу данных через потоки. Заметим, что с использованием этих классов можно работать не только с потоками байт, но также и с потоками данных других типов, например числами int или текстовыми строками.
Библиотека классов java.net
Язык программирования Java разрабатывался в предположении, что им будут пользоваться для создания сетевых приложений. Поэтому было бы странно, если бы в составе среды разработки приложений Java не поставлялась библиотека классов для работы в сети. Библиотека классов java.net предназначена как раз для этого. Она содержит классы, с помощью которых можно работать с универсальными сетевыми адресами URL, передавать данные с использованием сокетов TCP и UDP, выполнять различные операции с адресами IP. Эта библиотека содержит также классы для выполнения преобразований двоичных данных в текстовый формат, что часто бывает необходимо.
В качестве примера приложения, составленного на языке программирования Java и ориентированного на работу в сети Internet, можно привести игру Java Color Lines (рис. 1.3).
Рис. 1.3. Сетевая игра Java Color Lines, расположенная на сервере http://spektr.orc.ru
Это сетевая версия извстной игры Lines, которая выполнена в виде нескольких аплетов, взаимодействующих между собой и между сервером WWW с адресом http://spektr.orc.ru, на котором они расположены. Так как список имен игроков и достигнутых ими результатов хранится на сервере, вы можете поучаствовать в мировом турнире, сразившись с игроками из разных стран.
В следующем томе “Библиотеки системного программиста”, посвященном Java, мы расскажем о том, как организовать взаимодействие между аплетами и сервером WWW.
Библиотека классов java.util
Библиотека классов java.util очень полезна при составлении приложений, так как в ней имеются классы для создания таких структур, как динамические массивы, стеки и словари. Есть классы для работы с генератором псевдослучайных чисел, для разбора строк на составляющие элементы (токены), для работы с календарной датой и временем.
Библиотеки классов Java
Если предоставить в распоряжение программиста только язык программирования и не снабдить его набором готовых модулей, предназначенных для решения самых распространенных задач, ему придется отвлекаться на множество мелких деталей. Обычно все профессиональные системы разработки приложений содержат в своем составе набор стандартных библиотечных функций или библиотеки классов, таких как Microsoft MFC или Borland OWL. В комплекте со всеми средствами разработки Java поставляются достаточно развитые библиотеки классов, значительно упрощающие программирование. В этом разделе мы кратко расскажем о составе и назначении библиотек классов Java.
Благодарности
В работе над книгой нам помогали сотрудники фирмы Interactive Products Inc. Максим Синев и Сергей Ноженко, у которых мы консультировались по различным вопросам.
Мы признательны генеральному директору АО “ДиалогНаука” Антимонову Сергею Григорьевичу и его заместителю Лященко Юрию Павловичу за возможность размещения информации о наших книгах на сервере Web по адресу http://www.dials.ccas.ru/frolov, а также за возможность доступа к сети Internet через сервер АО “ДиалогНаука”.
Мы также благодарим корректора Кустова В. С. и сотрудников издательского отдела АО “Диалог-МИФИ” Голубева О. А., Голубева А. О., Дмитриеву Н. В., Виноградову Е. К., Кузьминову О. А.
Добавление компонент в панели
Для добавления компонент в панель вы должны указать, для какой панели вызывается метод add, например:
Botton btn1;
Botton btn2;
btn1 = new Button();
btn2 = new Button();
pBottomPanel.add(btn1);
pBottomPanel.add(btn2);
Добавление панелей
Создав панели, вы можете добавить их в окно аплета, вызвав метод add, как это показано ниже:
add(pTopPanel);
add(pBottomPanel);
Заметим, что вы можете добавлять панели в панели, указывая, для какой панели нужно вызывать метод add:
Panel pLeft;
Panel pRight;
pLeft = new Panel();
pRight = new Panel();
pTopPanel.setLayout(new GridLayout(1, 2));
pTopPanel.add(pLeft);
pTopPanel.add(pRight);
Здесь мы создали две панели pLeft и pRight, которые по нашему замыслу должны разделить пространство панели pTopPanel на две части по вертикали. Для обеспечения вертикального размещения панелей pLeft и pRight в панели pTopPanel мы вызвали для панели pTopPanel метод setLayout. При этом мы указали, что компоненты, добавляемые в эту панель, должны размещаться в таблице, состоящей из односй строки и двух столбцов.
Затем панели pLeft и pRight были добавлены в панель pTopPanel методом add.
Другие встроенные классы
Среди других встроенных классов отметим класс Math, предназначенный для выполнения математических операций, таких как взятие синуса, косинуса и тангенса.
Предусмотрены также классы для выполнения запуска процессов и задач, управления системой безопасности, а также для решения прочих системных задач.
Библиотека встроенных классов содержит очень важные классы для работы с исключениями. Эти классы нужны для обработки ошибочных ситуаций, которые могут возникнуть (и возникают!) при работе приложений или аплетов Java.
Файл HelloAp.java
Исходный текст аплета HelloAp начинается с двух строк, подключающих оператором import библиотеки классов:
import java.applet.*;
import java.awt.*;
Оператор import должен располагаться в файле исходного текста перед другими операторами (за исключением операторов комментария). В качестве параметра оператору import передается имя подключаемого класса из библиотеки классов. Если же необходимо подключить все классы данной библиотеки (как в нашем случае), вместо имени класса указывается символ“*”.
Мы уже перечисляли библиотеки классов Java. Напомним, что библиотека java.applet содержит классы, необходимые для создания аплетов, то есть разновидности приложений Java, встраиваемых в документы HTML и работающих под управлением навигатора Internet. С помощью классов библиотеки java.awt аплет может выполнять в своем окне рисование различных изображений или текста, причем данный метод рисования не зависит от платформы, на которой работает аплет.
Далее в исходном тексте аплета определяется класс типа public с именем HelloAp, которое должно обязательно совпадать с именем файла, содержащего исходный текст этого класса:
public class HelloAp extends Applet
{
. . .
}
Определенный нами класс HelloAp с помощью ключевого слова extends наследуется от класса Applet. При этом методам класса HelloAp становятся доступными все методы и данные класса, за исключением определенных как private. Класс Applet определен в библиотеке классов java.applet, которую мы подключили оператором import.
Создавая файл HelloAp.java, система Java Applet Wizard определили в классе HelloAp конструктор и несколько методов, заменив некоторые методы базового класса Applet.
Файл HelloApp.html
Файл HelloApp.html автоматически создается системой Java Applet Wizard, если это было указано во второй диалоговой панели, задающей параметры нового проекта.
Нас интересует в этом файле оператор
, который используется в паре с оператором и предназначен для встраивания окна аплета в документ HTML.
Вот как выглядит фрагмент документа HTML, созданного для нашего проекта, в котором встраивается аплет:
code=HelloAp.class
id=HelloAp
width=320
height=240 >
Рассмотрим параметры оператора
, указанные в этом фрагменте кода, а также некоторые другие.
Параметр
Описание
ALIGN
Выравнивание окна аплета относительно окружающего его текста. Возможны следующие значения:
LEFT выравнивание влево относительно окружающего текста;
CENTER центрирование;
RIGHT выравнивание вправо относительно окружающего текста;
TOP выравнивание по верхней границе;
MIDDLE центрирование по вертикали;
BOTTOM выравнивание по нижней границе
ALT
С помощью этого параметра можно задать текст, который будет отображаться в окне аплета в том случае, если навигатор не может работать с аплетами Java
CODE
Имя двоичного файла, содержащего код аплета. По умолчанию путь к этому файлу указывается относительно каталога с файлом HTML, в который встроен аплет. Такое поведение может быть изменено параметром CODEBASE
CODEBASE
Базовый адрес URL аплета, то есть путь к каталогу, содержащему аплет
HEIGHT
Начальная ширина окна аплета в пикселах
WIDTH
Начальная высота окна аплета в пикселах
HSPACE
Зазор слева и справа от окна аплета
VSPACE
Зазор сверху и снизу от окна аплета
NAME
Идентификатор аплета, который может быть использован другими аплетами, расположенными в одном и том же документе HTML
TITLE
Строка заголовка
Дополнительно между операторами и вы можете задать параметры аплета. Для этого используется оператор , который мы рассмотрим позже.
Внешний вид окна навигатора, в котором отображается созданный документ HTML с аплетом, показан на рис. 2.15.
Рис. 2.15. Окно аплета в документе HTML
Обратите внимание, что границы окна аплета никак не выделяются, а цвет его фона совпадает с цветом фона документа HTML. В следующей главе мы научим вас изменять цвет фона окна аплета и текста, отображаемого в этом окне.
В нижней части окна под разделительной линией находится ссылка на исходный текст аплета:
The source.
Вы можете использовать эту ссылку для просмотра содержимого файла HelloAp.java.
Интерфейсы
Интерфейсы создаются при помощи ключевого слова interface таким же образом, что и классы. Однако в отличие от последних, интерфейсы являются аналогом абстрактных базовых классов без полей данных и предназначены только для определений набора методов для решения каких-либо задач, например, добавления компонент в контейнеры, организации списков, сортировки и так далее.
Вы можете создать свой класс на базе другого класса, указав при этом с помощью ключевого слова implements, что он реализует тот или иной интерфейс. При этом наряду с методами базового класса в созданном таким образом классе будут доступны методы, определенные в интерфейсе.
Исходные файлы аплета HelloAp
Как мы уже говорили, в результате работы системы Java Applet Wizard созданы файлы HelloAp.java и HelloAp.html. Рассмотрим их по отдельности.
Исходные файлы приложения Painter
В листинге 3.1 мы привели исходный текст аплета Painter.
Листинг 3.1. Файл Painter\Painter.java
// =========================================================
// Аплет, демонстрирующий использование различных
// функций рисования
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Painter extends Applet
{
// -------------------------------------------------------
// Painter
// Конструктор не используется
// -------------------------------------------------------
public Painter()
{
}
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Painter\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Вызывается во время инициализации аплета
// -------------------------------------------------------
public void init()
{
// Для того чтобы размеры окна аплета можно было
// задавать в документе HTML, закрываем следующую
// строку символом комментария
// resize(320, 240);
}
// -------------------------------------------------------
// destroy
// Метод destroy не используется
// -------------------------------------------------------
public void destroy()
{
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Эта строка будет удалена из окна аплета
// методом clearRect
g.drawString("Невидимая строка", 10, 20);
// Стираем содержимое окна аплета. Цвет окна
// становится таким же, как и цвет фона
// окна навигатора
g.clearRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Пишем строку в верхнем левом углу окна аплета
g.drawString("Привет из аплета!", 10, 20);
// Рисуем в цикле пять горизонтальных линий
for(int i = 0; i < 5; i++)
{
g.drawLine(10, 30 + (i * 10), 200, 30 + (i * 10));
}
// Копируем область окна, занятую
// нарисованными линиями
g.copyArea(10, 30, 200, 50, 220, 0);
// Выбираем в контекст отображения белый цвет
g.setColor(Color.white);
// Закрашиваем белвым цветом нижнюю часть окна
g.fillRect(1, 80,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 81);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем два трехмерных прямоугольника
g.draw3DRect(10, 100, 100, 20, true);
g.draw3DRect(120, 100, 100, 20, false);
// Выбираем в контекст отображения красный цвет
g.setColor(Color.red);
// Рисуем рамку, в которую будет вписан сегмент
g.drawRect(10, 140, 200, 100);
g.drawLine(10, 190, 210, 190);
g.drawLine(110, 140, 110, 240);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем сегмент
g.drawArc(10, 140, 200, 100, -25, 320);
// Создаем многоугольник
Polygon p = new Polygon();
// Добавляем в многоугольник несколько вершин
p.addPoint(270, 239);
p.addPoint(350, 230);
p.addPoint(360, 180);
p.addPoint(390, 160);
p.addPoint(340, 130);
p.addPoint(270, 239);
// Рисуем многоугольник
g.drawPolygon(p);
// Рисуем прямоугольник с закругленными углами
g.drawRoundRect(10, 250, 200, 100, 60, 40);
// Выбираем в контекст отображения красный цвет
g.setColor(Color.red);
// Задаем область ограничения вывода
g.clipRect(260, 250, 200, 100);
// Рисуем круг, пересекающий область ограничения
g.fillOval(300, 200, 170, 170);
}
// -------------------------------------------------------
// start
// Метод start не используется
// -------------------------------------------------------
public void start()
{
}
// -------------------------------------------------------
// stop
// Метод stop не используется
// -------------------------------------------------------
public void stop()
{
}
}
Листинг 3. 2 содержит исходный текст документа HTML, в который встроен данный аплет.
Листинг 3.2. Файл Painter\Painter.html
Painter
code=Painter.class
id=Painter
width=500
height=400>
The source.
Мы внесли небольшие изменения в файл Painter.html, подготовленный системой Java Applet Wizard. Эти изменения заключаются в увеличении размеров окна аплета, задаваемых параметрами WIDTH и HEIGHT оператора .
Исходные тексты приложения ButtonPress
Исходный текст приложения ButtonPress приведен в листинге 5.1.
Листинг 5.1. Файл ButtonPress\ButtonPress.java
// =========================================================
// Работа с кнопками
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ButtonPress extends Applet
{
// Создаем четыре ссылки на объекты типа Button
Button btn1;
Button btn2;
Button btn3;
Button btn4;
// Строка для записи названия нажатой кнопки
String sTextLabel;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающий строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ButtonPress\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем четыре кнопки
btn1 = new Button("Button 1");
btn2 = new Button("Button 2");
btn3 = new Button("Button 3");
btn4 = new Button("Button 4");
// Добавляем кнопки в контейнер, которым является
// окно аплета
add(btn1);
add(btn2);
add(btn3);
add(btn4);
// Название кнопки, нажатой в последний раз
sTextLabel = new String("");
}
// -------------------------------------------------------
// action
// Метод вызывается, когда пользователь выполняет
// действие над компонентами
// -------------------------------------------------------
public boolean action(Event evt, Object obj)
{
// Ссылка на кнопку, от которой пришло сообщение
Button btn;
// Проверяем, что событие вызвано кнопкой, а не
// другим компонентом
if(evt.target instanceof Button)
{
// Получам ссылку на кнопку, вызвавшую событие
btn = (Button)evt.target;
// Получаем название кнопки
sTextLabel = btn.getLabel();
// Выполняем ветвление по кнопкам. Для каждой кнопки
// записываем ее название
// в строку состояния навигатора
if(evt.target.equals(btn1))
{
showStatus(
"Button 1 (\"" + sTextLabel + "\") pressed");
}
else if(evt.target.equals(btn2))
{
showStatus(
"Button 2 (\"" + sTextLabel + "\") pressed");
}
else if(evt.target.equals(btn3))
{
showStatus(
"Button 3 (\"" + sTextLabel + "\") pressed");
}
else if(evt.target.equals(btn4))
{
showStatus(
"Button 4 (\"" + sTextLabel + "\") pressed");
}
// Если событие возникло от неизвестной кнопки,
// мы его не обрабатываем
else
{
return false;
}
// Перерисовываем окно аплета
repaint();
// возвращаем признак того, что мы обработали событие
return true;
}
// Если событие вызвано не кнопкой,
// мы его не обрабатываем
return false;
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Рисуем название нажатой кнопки
g.drawString("Button (\"" + sTextLabel + "\") pressed",
10, 70);
}
}
Исходный текст документа HTML, созданный для аплета ButtonPress, представлен в листинге 5.2.
Листинг 5.2. Файл ButtonPress\ButtonPress.html
ButtonPress
code=ButtonPress.class
id=ButtonPress
width=320
height=240 >
The source.
Исходные тексты приложения CheckBoxes
Файл исходного текста приложения CheckBoxes представлен в листинге 5.3.
Листинг 5.3. Файл CheckBoxes\CheckBoxes.java
// =========================================================
// Работа с переключателями
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class CheckBoxes extends Applet
{
// Создаем три ссылки на объекты типа Checkbox
Checkbox chbox1;
Checkbox chbox2;
Checkbox chbox3;
// Создаем ссылку на объект типа CheckboxGroup
CheckboxGroup grModeGroup;
// Создаем четыре ссылки на объекты типа Checkbox
Checkbox rdbox1;
Checkbox rdbox2;
Checkbox rdbox3;
Checkbox rdbox4;
// Создаем ссылку на объект типа Button
Button btnGet;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: CheckBoxes\r\n" +
"WWW: http://www.glasnet.ru/~frolov" +
"E-mail: frolov@glas.apc.org" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Устанавливаем желтый цвет фона
setBackground(Color.yellow);
// Создаем три переключателя с независимой фиксацией
chbox1 = new Checkbox("Switch 1");
chbox2 = new Checkbox("Switch 2");
chbox3 = new Checkbox("Switch 3");
// Создаем группу переключателей с зависимой фиксацией
grModeGroup = new CheckboxGroup();
// Создаем четыре переключателя с зависимой фиксацией,
// принадлежащие группе grModeGroup
rdbox1 = new Checkbox("Mode 1",grModeGroup, true);
rdbox2 = new Checkbox("Mode 2",grModeGroup, false);
rdbox3 = new Checkbox("Mode 3",grModeGroup, false);
rdbox4 = new Checkbox("Mode 4",grModeGroup, false);
// Создаем кнопку, предназначенную для определения
// текущего состояния переключателей
btnGet = new Button("Get CheckBoxes state");
// Добавляем в окно аплета четыре переключателя
// с зависимой фиксацией
add(rdbox1);
add(rdbox2);
add(rdbox3);
add(rdbox4);
// Добавляем в окно аплета три переключателя
// с независимой фиксацией
add(chbox1);
add(chbox2);
add(chbox3);
// Добавляем в окно аплета кнопку
add(btnGet);
}
// -------------------------------------------------------
// action
// Метод вызывается, когда пользователь выполняет
// действие над компонентами
// -------------------------------------------------------
public boolean action(Event evt, Object obj)
{
// Проверяем, что событие вызвано кнопкой, а не
// другим компонентом
if(evt.target instanceof Button)
{
// Выполняем ветвление по кнопкам.
if(evt.target.equals(btnGet))
{
showStatus("Button 1 pressed");
}
// Если событие возникло от неизвестной кнопки,
// мы его не обрабатываем
else
{
return false;
}
// Перерисовываем окно аплета
repaint();
// возвращаем признак того, что мы обработали событие
return true;
}
// Если событие вызвано не кнопкой,
// мы его не обрабатываем
return false;
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Строка для записи списка
// состояния переключателей
String s = new String("> ");
// Проверяем переключатели с независимой фиксацией
if(chbox1.getState())
s = s + chbox1.getLabel() + ", ";
if(chbox2.getState())
s = s + chbox2.getLabel() + ", ";
if(chbox3.getState())
s = s + chbox3.getLabel() + ", ";
if(rdbox1.getState())
s = s + rdbox1.getLabel();
// Проверяем переключатели с зависимой фиксацией
else if(rdbox2.getState())
s = s + rdbox2.getLabel();
else if(rdbox3.getState())
s = s + rdbox3.getLabel();
else if(rdbox4.getState())
s = s + rdbox4.getLabel();
// Рисуем строку состояния переключателей
g.drawString(s, 10, 150);
}
}
В листинге 5. 4 вы найдете исходный текст документа HTML, который был создан системой Java Applet Wizard для нашего аплета.
Листинг 5.4. Файл CheckBoxes\CheckBoxes.html
CheckBoxes
code=CheckBoxes.class
id=CheckBoxes
width=320
height=240 >
The source.
Исходные тексты приложения ChoiceList
Исходный текст приложения ChoiceList вы найдете в листинге 5.5.
Листинг 5.5. Файл ChoiceList\ChoiceList.java
// =========================================================
// Списки типа Drop Down
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ChoiceList extends Applet
{
// Создаем ссылки на объекты класса Choice
Choice chBackgroundColor;
Choice chForegroundColor;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ChoiceList\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем списки для выбора цвета фона и
// цвета изображения
chBackgroundColor = new Choice();
chForegroundColor = new Choice();
// Добавляем списки в окно аплета
add(chBackgroundColor);
add(chForegroundColor);
// Заполняем список цвета фона
chBackgroundColor.addItem("Yellow");
chBackgroundColor.addItem("Green");
chBackgroundColor.addItem("White");
// Заполняем список цвета изображения
chForegroundColor.addItem("Black");
chForegroundColor.addItem("Red");
chForegroundColor.addItem("Blue");
// Устанавливаем цвет фона
setBackground(Color.yellow);
// Устанавливаем цвет изображения
setForeground(Color.black);
}
// -------------------------------------------------------
// action
// Метод вызывается, когда пользователь выполняет
// действие над компонентами
// -------------------------------------------------------
public boolean action(Event evt, Object obj)
{
// Переменная для хранения ссылки на список,
// вызвавший событие
Choice ch;
// Получаем ссылку на список
ch = (Choice)evt.target;
// Проверяем, что событие вызвано списком, а не
// другим компонентом
if(evt.target instanceof Choice)
{
// Выполняем ветвление по спискам
// Список цвета фона
if(evt.target.equals(chBackgroundColor))
{
// Получаем номер текущего элемента списка
// и устанавливаем соответствующий
// цвет фона
if(ch.getSelectedIndex() == 0)
setBackground(Color.yellow);
else if(ch.getSelectedIndex() == 1)
setBackground(Color.green);
else if(ch.getSelectedIndex() == 2)
setBackground(Color.white);
}
// Список цвета изображения
else if(evt.target.equals(chForegroundColor))
{
// Получаем номер текущего элемента списка
// и устанавливаем соответствующий
// цвет изображения
if(ch.getSelectedIndex() == 0)
setForeground(Color.black);
else if(ch.getSelectedIndex() == 1)
setForeground(Color.red);
else if(ch.getSelectedIndex() == 2)
setForeground(Color.blue);
}
// Если событие возникло от неизвестного списка,
// мы его не обрабатываем
else
{
return false;
}
// Перерисовываем окно аплета
repaint();
// возвращаем признак того, что мы обработали событие
return true;
}
// Если событие вызвано не кнопкой,
// мы его не обрабатываем
return false;
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Рисуем строку
g.drawString("Смотри на цвет фона и текста!", 10, 50);
}
}
Исходный текст документа HTML, созданного для нашего аплета, приведен в листинге 5.6.
Листинг 5.6. Файл ChoiceList\ChoiceList.html
ChoiceList
code=ChoiceList.class
id=ChoiceList
width=320
height=240 >
The source.
Исходные тексты приложения KeyCode
Файл исходного текста приложения KeyCode приведен в листинге 4.5.
Листинг 4.5. Файл KeyCode\KeyCode.java
// =========================================================
// Просмотр кодов клавиш
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class KeyCode extends Applet
{
// Высота символов текущего шрифта
int yHeight;
// Текущие размеры окна аплета
Dimension dimAppWndDimension;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: KeyCode\r\n" +
"Author: Alexandr Frolov\r\n" +
"WWW: http://www.glasnet.ru/~frolov" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Получаем контекст отображения
Graphics g = getGraphics();
// Определяем метрики текущего шрифта
FontMetrics fm = g.getFontMetrics();
// Сохраняем полную высоту символов шрифта
yHeight = fm.getHeight();
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
}
// -------------------------------------------------------
// keyDown
// Вызывается, когда пользователь нажимает на клавишу
// -------------------------------------------------------
public boolean keyDown(Event evt, int nKey)
{
// Массив для преобразования кода символа в сивол
char[] chKey;
// Временная строка
String s;
// Создаем массив из одного элемента
chKey = new char[1];
// Записыаем в него код нажатой клавиши
chKey[0] = (char)nKey;
// Преобразуем в строку
s = new String(chKey);
// Получаем контекст отображения
Graphics g = getGraphics();
// Выбираем черный цвет для рисования
g.setColor(Color.black);
// Рисуем символ, соответствующий нажатой клавише
g.drawString(s + " ", 10, 10);
// Рисуем код клавиши
g.drawString(
" -> key: " + evt.key, 20, 10);
// Рисуем код модификации
g.drawString(" mod: " + evt.modifiers, 100, 10);
return true;
}
// -------------------------------------------------------
// keyUp
// Вызывается, когда пользователь отпускает клавишу
// -------------------------------------------------------
public boolean keyUp(Event evt, int nKey)
{
// Получаем контекст отображения
Graphics g = getGraphics();
// Выполняем свертку нижней области окна
g.copyArea(0, 1,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - yHeight - 5, 0, yHeight + 1);
// Закрашиваем область ввода желтым цветом
g.setColor(Color.yellow);
g.fillRect(1, 1,
dimAppWndDimension.width - 2, yHeight + 1);
return true;
}
}
В листинге 4.6 вы найдете исходный текст документа HTML, в который встроен наш аплет.
Листинг 4.6. Файл KeyCode\KeyCode.html
KeyCode
code=KeyCode.class
id=KeyCode
width=320
height=240 >
The source.
Исходные тексты приложения TextOut
Файл исходного текста приложения TextOut представлен в листинге 3.5.
Листинг 3.5. Файл TextOut\TextOut.java
// =========================================================
// Установка различных шрифтов.
// Демонстрация способов передачи параметров в аплет
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class TextOut extends Applet
{
// -------------------------------------------------------
// Поля класса.
// Создаются автоматически для всех параметров аплета
// -------------------------------------------------------
private String m_Str1 = "Hello 1";
private String m_Str2 = "Hello 2";
private String m_Str3 = "Hello 3";
private String m_Str4 = "Hello 4";
private String m_Str5 = "Hello 5";
private String m_Font1 = "Arial";
private String m_Font2 = "Courier";
private String m_Font3 = "Times";
private String m_Font4 = "Helvetica";
private String m_Font5 = "Undefined";
private String m_Type1 = "Bold";
private String m_Type2 = "Italic";
private String m_Type3 = "Plain";
// -------------------------------------------------------
// Имена параметров
// -------------------------------------------------------
private final String PARAM_Str1 = "Str1";
private final String PARAM_Str2 = "Str2";
private final String PARAM_Str3 = "Str3";
private final String PARAM_Str4 = "Str4";
private final String PARAM_Str5 = "Str5";
private final String PARAM_Font1 = "Font1";
private final String PARAM_Font2 = "Font2";
private final String PARAM_Font3 = "Font3";
private final String PARAM_Font4 = "Font4";
private final String PARAM_Font5 = "Font5";
private final String PARAM_Type1 = "Type1";
private final String PARAM_Type2 = "Type2";
private final String PARAM_Type3 = "Type3";
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: TextOut\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// getParameterInfo
// Метод, возвращающий описание параметров
// -------------------------------------------------------
public String[][] getParameterInfo()
{
String[][] info =
{
{ PARAM_Str1, "String", "Text string to write" },
{ PARAM_Str2, "String", "Text string to write" },
{ PARAM_Str3, "String", "Text string to write" },
{ PARAM_Str4, "String", "Text string to write" },
{ PARAM_Str5, "String", "Text string to write" },
{ PARAM_Font1, "String", "Text font" },
{ PARAM_Font2, "String", "Text font" },
{ PARAM_Font3, "String", "Text font" },
{ PARAM_Font4, "String", "Text font" },
{ PARAM_Font5, "String", "Text font" },
{ PARAM_Type1, "String", "Font type" },
{ PARAM_Type2, "String", "Font type" },
{ PARAM_Type3, "String", "Font type" },
};
return info;
}
// -------------------------------------------------------
// init
// Вызывается во время инициализации аплета
// -------------------------------------------------------
public void init()
{
// Рабочая переменная для получения параметров
String param;
// Получение параметров и сохранение
// их значений в полях класса
// Строки, которые будут выведены в окно аплета
param = getParameter(PARAM_Str1);
if (param != null)
m_Str1 = param;
param = getParameter(PARAM_Str2);
if (param != null)
m_Str2 = param;
param = getParameter(PARAM_Str3);
if (param != null)
m_Str3 = param;
param = getParameter(PARAM_Str4);
if (param != null)
m_Str4 = param;
param = getParameter(PARAM_Str5);
if (param != null)
m_Str5 = param;
// Шрифты для отображения строк
param = getParameter(PARAM_Font1);
if (param != null)
m_Font1 = param;
param = getParameter(PARAM_Font2);
if (param != null)
m_Font2 = param;
param = getParameter(PARAM_Font3);
if (param != null)
m_Font3 = param;
param = getParameter(PARAM_Font4);
if (param != null)
m_Font4 = param;
param = getParameter(PARAM_Font5);
if (param != null)
m_Font5 = param;
// Начертание шрифтов
param = getParameter(PARAM_Type1);
if (param != null)
m_Type1 = param;
param = getParameter(PARAM_Type2);
if (param != null)
m_Type2 = param;
param = getParameter(PARAM_Type3);
if (param != null)
m_Type3 = param;
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Начальная координата для вывода по вертикали
int yStart = 20;
// Текущая координата для вывода строки
int yCurrent = 20;
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Получаем стиль шрифта и выбираем шрифт
// в соответствии с этим стилем
if(m_Type1.equals("Bold"))
g.setFont(new Font(m_Font1, Font.BOLD, 25));
else if(m_Type1.equals("Italic"))
g.setFont(new Font(m_Font1, Font.ITALIC, 25));
else if(m_Type1.equals("Plain"))
g.setFont(new Font(m_Font1, Font.PLAIN, 25));
// Отступ для первой строки
yCurrent += yStart;
// Рисуем первую строку
g.drawString(m_Str1, 10, yCurrent);
// Определяем метрики шрифта
FontMetrics fm = g.getFontMetrics();
// Устанавливаем новую текущую позицию для
// вывода очередной строки
yCurrent += fm.getHeight();
// Выбираем шрифт в контекст отображения
if(m_Type1.equals("Bold"))
g.setFont(new Font(m_Font2, Font.BOLD, 25));
else if(m_Type1.equals("Italic"))
g.setFont(new Font(m_Font2, Font.ITALIC, 25));
else if(m_Type1.equals("Plain"))
g.setFont(new Font(m_Font2, Font.PLAIN, 25));
// Рисуем вторую строку
g.drawString(m_Str2, 10, yCurrent);
// Устанавливаем новую текущую позицию для
// вывода очередной строки
fm = g.getFontMetrics();
yCurrent += fm.getHeight();
// Выбираем шрифт в контекст отображения
if(m_Type2.equals("Bold"))
g.setFont(new Font(m_Font3, Font.BOLD, 25));
else if(m_Type2.equals("Italic"))
g.setFont(new Font(m_Font3, Font.ITALIC, 25));
else if(m_Type2.equals("Plain"))
g.setFont(new Font(m_Font3, Font.PLAIN, 25));
// Рисуем третью строку
g.drawString(m_Str3, 10, yCurrent);
// Устанавливаем новую текущую позицию для
// вывода очередной строки
fm = g.getFontMetrics();
yCurrent += fm.getHeight();
// Выбираем шрифт в контекст отображения
if(m_Type2.equals("Bold"))
g.setFont(new Font(m_Font4, Font.BOLD, 25));
else if(m_Type2.equals("Italic"))
g.setFont(new Font(m_Font4, Font.ITALIC, 25));
else if(m_Type2.equals("Plain"))
g.setFont(new Font(m_Font4, Font.PLAIN, 25));
// Рисуем четвертую строку
g.drawString(m_Str4, 10, yCurrent);
// Устанавливаем новую текущую позицию для
// вывода очередной строки
fm = g.getFontMetrics();
yCurrent += fm.getHeight();
// Выбираем шрифт в контекст отображения
if(m_Type3.equals("Bold"))
g.setFont(new Font(m_Font5, Font.BOLD, 25));
else if(m_Type3.equals("Italic"))
g.setFont(new Font(m_Font5, Font.ITALIC, 25));
else if(m_Type3.equals("Plain"))
g.setFont(new Font(m_Font5, Font.PLAIN, 25));
// Рисуем пятую строку
g.drawString(m_Str5, 10, yCurrent);
}
}
Исходный текст документа HTML, в который встроен аплет TextOut, приведен в листинге 3.6.
Листинг 3.6. Файл TextOut\TextOut.html
TextOut
code=TextOut.class
id=TextOut width=320
height=240 >
The source.
Исходные тексты приложения
Файл исходного текста приложения MouseClick представлен в листинге 4.1.
Листинг 4.1. Файл MouseClick\MouseClick.java
// =========================================================
// Обработка событий от мыши
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class MouseClick extends Applet
{
// Текущие координаты курсора при нажатии на
// кнопку мыши
Dimension dimMouseCursor;
// Временная переменная для хранения события
Event ev;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MouseClick\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
Исходный текст аплета LineDraw приведен в листинге 4.3.
Листинг 4.3. Файл LineDraw\LineDraw.java
// =========================================================
// Рисование прямых линий
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// Добавляем для класса Vector
import java.util.*;
public class LineDraw extends Applet
{
// Координаты курсора при нажатии кнопки мыши
Dimension dmDown;
// Координаты курсора при отжатии кнопки мыши
Dimension dmUp;
// Предыдущие координаты конца линии
Dimension dmPrev;
// Признак включения режима рисования
boolean bDrawing;
// Массив координат линий
Vector lines;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: LineDraw\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Сброс признака рисования
bDrawing = false;
// Создание массива для хранения координат линий
lines = new Vector();
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
Файл исходных текстов приложения ListBox приведен в листинге 5.7.
Листинг 5.7. Файл ListBox\ListBox.java
// =========================================================
// Списки типа List Box
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ListBox extends Applet
{
// Создаем ссылки на объекты класса List
List chBackgroundColor;
List chForegroundColor;
// Строки для хранения названий выбираемого цвета
String sSelBackground = new String("Yellow");
String sSelForeground = new String("Black");
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ListBox\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем списки для выбора цвета фона и
// цвета изображения
chBackgroundColor = new List(6, false);
chForegroundColor = new List(4, false);
// Добавляем списки в окно аплета
add(chBackgroundColor);
add(chForegroundColor);
// Заполняем список цвета фона
chBackgroundColor.addItem("Yellow");
chBackgroundColor.addItem("Green");
chBackgroundColor.addItem("White");
chBackgroundColor.addItem("Black");
Исходный текст приложения TextLabel приведен в листинге 5.9.
Листинг 5.9. Файл TextLabel\TextLabel.java
// =========================================================
// Работа с компонентами Label
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class TextLabel extends Applet
{
// Создаем ссылку на объекты типа Label
Label lbTextLabel;
// Создаем три ссылки на объекты типа Button
Button btnLeft;
Button btnCenter;
Button btnRight;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: TextLabel\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем компоненту Label
lbTextLabel = new Label("Выберите выравнивание");
// Создаем три кнопки
btnLeft = new Button("Влево");
btnCenter = new Button("Центровка");
btnRight = new Button("Вправо");
// Добавляем три кнопки
add(btnLeft);
add(btnCenter);
add(btnRight);
// Добавляем компоненту Label
add(lbTextLabel);
// Устанавливаем цвет фона
setBackground(Color.yellow);
}
// -------------------------------------------------------
Исходные тексты приложения TxtField представлены в листинге 5.11.
Листинг 5.11. Файл TxtField\TxtField.java
// =========================================================
// Однострочное текстовое поле класса TextField
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class TxtField extends Applet
{
// Создаем ссылку на объекты типа TextField
TextField txt;
// Создаем ссылку на объекты типа Button
Button btnGetText;
// Строка для хранения введенных данных
String str;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: TxtField\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем редактируемое однострочное текстовое поле
txt = new TextField("Введите строку текста", 35);
// Создаем кнопку, с помощью которой можно получить
// содержимое текстового поля
btnGetText = new Button("Получить строку");
// Добавляем текстовое поле в окно аплете
add(txt);
// Добавляем кнопку в окно аплете
add(btnGetText);
// Получаем и сохраняем текущий текст,
// установленный в поле
str = txt.getText();
Исходный текст приложения приведен в листинге 5.13.
Листинг 5.13. Файл TextEdit\TextEdit.java
// =========================================================
// Многострочное текстовое поле класса TextArea
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class TextEdit extends Applet
{
// Создаем ссылку на объект типа TextArea
TextArea txt;
// Создаем ссылку на объекты типа Button
Button btnGetText;
Button btnGetSelectedText;
String str;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: TextEdit\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем редактируемое многострочное текстовое поле
txt = new TextArea("Введите строку текста", 5, 35);
// Создаем кнопку, с помощью которой можно получить
// полное содержимое текстового поля
btnGetText = new Button("Получить все");
// Создаем кнопку, с помощью которой можно получить
// содержимое выделенной области текстового поля
btnGetSelectedText = new Button("Получить выделенное");
// Добавляем текстовое поле в окно аплете
add(txt);
Исходный текст аплета Grid приведен в листинге 6.1.
Листинг 6.1. Файл Grid\Grid.java
// =========================================================
// Режим компоновки GridLayout
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Grid extends Applet
{
// Создаем ссылки на объекты типа Button
Button btn1;
Button btn2;
Button btn3;
Button btn4;
Button btn5;
Button btn6;
Button btn7;
Button btn8;
// Строка для записи названия нажатой кнопки
String sTextLabel;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Grid\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.1";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Устанавливаем желтый цвет фона
setBackground(Color.yellow);
// Создаем кнопки
btn1 = new Button("Button 1");
btn2 = new Button("Button 2");
btn3 = new Button("Button 3");
btn4 = new Button("Button 4");
btn5 = new Button("Button 5");
btn6 = new Button("Button 6");
btn7 = new Button("Button 7");
btn8 = new Button("Button 8");
// Устанавливаем режим GridLayout
Исходный текст приложения Border приведен в листинге 6.3.
Листинг 6.3. Файл Border\Border.java
// =========================================================
// Режим компоновки BorderLayout
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Border extends Applet
{
// Создаем ссылки на объекты типа Button
Button btn1;
Button btn2;
Button btn3;
Button btn4;
Button btn5;
// Строка для записи названия нажатой кнопки
String sTextLabel;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Grid\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.1";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Устанавливаем желтый цвет фона
setBackground(Color.yellow);
// Создаем кнопки
btn1 = new Button("Button North");
btn2 = new Button("Button East");
btn3 = new Button("Button West");
btn4 = new Button("Button South");
btn5 = new Button("Button Center");
// Устанавливаем режим GridLayout
setLayout(new BorderLayout());
// Добавляем кнопки в контейнер
add("North", btn1);
add("East", btn2);
Файл исходного текста приложения PanelDemo представлен в листинге7.1.
Листинг 7.1. Файл PanelDemo\PanelDemo.java
// =========================================================
// Работа с панелями класса Panel
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class PanelDemo extends Applet
{
// Панель для размещения блокнота
Panel pCardPanel;
// Панель для размещения кнопок управления блокнотом
Panel pButtonPanel;
// Создаем ссылки на объекты типа Button
Button btn1;
Button btn2;
Button btn3;
Button btn4;
Button btn5;
Button btnNext;
Button btnPrev;
// Строка для записи названия нажатой кнопки
String sTextLabel;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Grid\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.1";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Устанавливаем желтый цвет фона
setBackground(Color.yellow);
// Создаем в окне аплета две панели, разделяющие
// окно по горизонтали. В верхней панели будет
// находиться блокнот,
// в нижней - кнопки управления блокнотом
setLayout(new GridLayout(2, 1));
// Создаем кнопки блокнота
Исходный текст приложения Notebook приведен в листинге 7.3.
Листинг 7.3. Файл Notebook\Notebook.java
// =========================================================
// Набор диалоговых панелей
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Notebook extends Applet
{
// Панель для размещения блокнота
Panel pCardPanel;
// Панель для размещения кнопок управления блокнотом
Panel pButtonPanel;
// Панель для рисования
Panel pDraw;
// Панели отдельных страниц
Panel pBackgroundColor; // страница выбора цвета фона
Panel pForegroundColor; // страница выбора цвета
// изображения
Panel pFont; // страница выбора шрифта
// Кнопки выбора сраниц блокнота
Button btnNext; // следующая
Button btnPrev; // предыдущая
Button btnBackgroundColor; // фон
Button btnForegroundColor; // изображение
Button btnFont; // шрифт
// Создаем ссылки на объекты класса Choice
Choice chBackgroundColor; // список цветов фона
Choice chForegroundColor; // список цветов изображения
Choice chFont; // список шрифтов
// Текстовые метки списков
Label tBackgroundColor; // метка списка цветов фона
Label tForegroundColor; // метка списка цветов
// изображения
Label tFont; // метка списка шрифтов
// Строка для хранения название выбранного шрифта
String sFontName;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
Файл исходных текстов приложения приведен в листинге 7.5.
Листинг 7.5. Файл Panel2\Panel2.java
// =========================================================
// Работа с панелями класса Panel
// Наследование от класса Panel
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// =========================================================
// Класс Panel2
// Это наш аплет
// =========================================================
public class Panel2 extends Applet
{
// Первая панель
FirstPanel pPanel1;
// Вторая панель
SecondPanel pPanel2;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Panel2\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем в окне аплета две панели, разделяющие
// окно по горизонтали
setLayout(new GridLayout(2, 1));
// Создаем первую панель
pPanel1 = new FirstPanel();
// Добавляем первую панель в окно аплета
add(pPanel1);
// Создаем вторую панель
pPanel2 = new SecondPanel();
// Добавляем вторую панель
add(pPanel2);
// Устанавливаем желтый цвет фона для первой панели
pPanel1.setBackground(Color.yellow);
Исходный текст приложения FrameWnd приведен в листинге 8.1.
Листинг 8.1. Файл FrameWnd\FrameWnd.java
// =========================================================
// Работа с окнами и диалоговыми панелями
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// =========================================================
// Класс FrameWnd
// Это наш аплет
// =========================================================
public class FrameWnd extends Applet
{
// Окно, которое будет возникать из аплета
MainFrameWnd fMainFrame;
// Кнопка для отображения окна fMainFrame
Button btnShowFrame;
// Кнопка для удаления окна fMainFrame
Button btnHideFrame;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: FrameWnd\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем новое окно на базе класса MainFrameWnd
fMainFrame = new MainFrameWnd("Main Frame Window");
// Создаем кнопку для отображения этого окна
btnShowFrame = new Button("Show Frame Window");
// Добавляем кнопку в окно аплета
add(btnShowFrame);
// Создаем кнопку для удаления окна fMainFrame
Исходный текст приложения
Исходный текст приложения представлен в листинге 3.3.
Листинг 3.3. Файл FontList\FontList.java
// =========================================================
// Просмотр списка доступных шрифтов
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class FontList extends Applet
{
// -------------------------------------------------------
// Поля класса
// -------------------------------------------------------
Toolkit toolkit; // ссылка на Toolkit
String fntlist[]; // список шрифтов
int yStart = 20; // координата Y начала области вывода
int yStep; // шаг вывода строк с названиями шрифтов
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: FontList\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Вызывается во время инициализации аплета
// -------------------------------------------------------
public void init()
{
// Получаем ссылку на Toolkit
toolkit = Toolkit.getDefaultToolkit();
// Получаем список доступных шрифтов
fntlist = toolkit.getFontList();
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем метрики шрифта
FontMetrics fm = g.getFontMetrics();
// Устанавливаем шаг вывода строк по вертикали
// равным полной высоте символов текущего шрифта
yStep = fm.getHeight();
// Устанавливаем новую высоту аплета исходя
// из количества элементов в списке шрифтов
resize(150, 20 + yStep * fntlist.length);
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выводим в цикле список установленных шрифтов
for(int i = 0; i < fntlist.length; i++)
{
g.drawString(fntlist[i], 10, yStart + yStep * i);
}
}
}
В листинге 3.4 вы найдете исходный текст документа HTML, в который встроен наш аплет.
Листинг 3.4. Файл FontList\FontList.html
FontList
code=FontList.class
id=FontList
width=320
height=240 >
The source.
Как обрабатываются события
Когда возникает событие, управление получает метод handleEvent из класса Component. Класс Applet является дочерним по отношению к классу Component.
Прототип метода handleEvent мы привели ниже:
public boolean handleEvent(Event evt);
В качестве параметра методу handleEvent передается объект класса Event, который содержит всю информацию о событии. По содержимому полей класса Event вы можете определить координаты курсора мыши в момент, когда пользователь нажал клавишу, отличить одинарный щелчок от двойного и так далее.
Ниже мы привели список полей класса Event, которые вы можете проанализировать:
Поле
Описание
public Object arg;
Произвольный аргумент события, значение которого зависит от типа события
public int clickCount;
Это поле имеет значение только для события с типом MOUSE_DOWN и содержит количество нажатий на клавишу мыши. Если пользователь сделал двойной щелчок мышью, в это поле будет записано значение 2
public Event evt;
Следующее событие в связанном списке
public int id;
Тип события. Ниже мы перечислим возможные значения для этого поля
public int key;
Код нажатой клавиши (только для события, созданного при выполнении пользователем операции с клавиатурой)
public int modifiers;
Состояние клавиш модификации , ,
public Object target;
Компонент, в котором произошло событие
public long when;
Время, когда произошло событие
public int x;
Координата по оси X
public int y;
Координата по оси Y
Поле id (тип события) может содержать следующие значения:
Значение
Тип события
ACTION_EVENT
Пользователь хочет, чтобы произошло некоторое событие
GOT_FOCUS
Компонент (в нашем случае окно аплета) получил фокус ввода. О фокусе ввода вы узнаете из раздела, посвященного работе с клавиатурой
KEY_ACTION
Пользователь нажал клавишу типа “Action”
KEY_ACTION_RELEASE
Пользователь отпустил клавишу типа “Action”
KEY_PRESS
Пользователь нажал обычную клавишу
KEY_RELEASE
Пользователь отпустил обычную клавишу
LIST_DESELECT
Отмена выделения элемента в списке
LIST_SELECT
Выделение элемента в списке
LOAD_FILE
Загрузка файла
LOST_FOCUS
Компонент потерял фокус ввода
MOUSE_DOWN
Пользователь нажал клавишу мыши
MOUSE_DRAG
Пользователь нажал клавишу мыши и начал выполнять перемещение курсора мыши
MOUSE_ENTER
Курсор мыши вошел в область окна аплета
MOUSE_EXIT
Курсор мыши покинул область окна аплета
MOUSE_MOVE
Пользователь начал выполнять перемещение курсора мыши, не нажимая клавишу мыши
MOUSE_UP
Пользователь отпустил клавишу мыши
SAVE_FILE
Сохранение файла
SCROLL_ABSOLUTE
Пользователь переместил движок полосы просмотра в новую позицию
SCROLL_LINE_DOWN
Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вниз
SCROLL_LINE_UP
Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вверх
SCROLL_PAGE_DOWN
Пользователь выполнил над полосой просмотра операцию сдвига на одну страницу вниз
SCROLL_PAGE_UP
Пользователь выполнил над полосой просмотра операцию сдвига на одну страницувверх
WINDOW_DEICONIFY
Пользователь запросил операцию восстановления нормального размера окна после его минимизации
WINDOW_DESTROY
Пользователь собирается удалить окно
WINDOW_EXPOSE
Окно будет отображено
WINDOW_ICONIFY
Окно будет минимизировано
WINDOW_MOVED
Окно будет перемещено
<
Если событие связано с клавиатурой (тип события KEY_ACTION или KEY_ACTION_RELEASE), в поле key может находиться одно из следующих значений:
Значение
Клавиша
DOWN
Клавиша перемещения курсора вниз
END
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
HOME
LEFT
Клавиша перемещения курсора влево
PGDN
PGUP
RIGHT
Клавиша перемещения курсора вправо
UP
Клавиша перемещения курсора вниз
Могут быть указаны следующие маски для поля модификаторов modifiers:
Значение маски
Описание
ALT_MASK
Была нажата клавиша
META_MASK
Была нажата мета-клавиша (клавиша для ввода диактрических символов)
CTRL_MASK
Была нажата клавиша
SHIFT_MASK
Была нажата клавиша
Ваше приложение может переопределить метод handleEvent и обрабатывать события самостоятельно, однако есть более простой путь. Обработчик этого метода, который используется по умолчанию, вызывает несколько методов, которые более удобны в использовании, в частности, при обработке событий от мыши или клавиатуры.
Как связаться с авторами
Полную информацию о всех наших книгах серий “Библиотека системного программиста” и “Персональный компьютер. Шаг за шагом”, а также дискеты к книгам, статьи и другую информацию вы можете найти в сети Internet на серверах Web по следующим адресам:
http://www.glasnet.ru/~frolov
http://www.dials.ccas.ru/frolov
Вы можете передать нам свои замечания и предложения по содержанию этой и других наших книг через электронную почту по адресам:
frolov@glas.apc.org
frolov.alexandr@usa.net
Если электронная почта вам недоступна, присылайте ваши отзывы в АО “Диалог-МИФИ” по адресу:
115409, Москва, ул. Москворечье, 31, корп. 2,
тел. 324-43-77
Приносим свои извинения за то что не можем ответить на каждое письмо. Мы также не занимаемся продажей и рассылкой книг, дискет, рекламы, отдельных фрагментов наших книг и исходных текстов к книгам, консультациями через электронную почту. По вопросам приобретения книг и дискет обращайтесь непосредственно в издательство “Диалог-МИФИ”.
Класс FirstPanel
Класс FirstPanel создан на базе класса Panel:
class FirstPanel extends Panel
{
public void paint(Graphics g)
{
. . .
super.paint(g);
}
}
В нашем приложении мы создаем верхнюю панель как объект этого класса. Единственный метод, переопределенный в классе FirstPanel - это метод paint.
Класс MainFrameWnd
Класс MainFrameWnd предназначен для создания автономного перекрывающегося окна, которое существует вне окна навигатора. Этот класс был нами создан на базе класса Frame:
class MainFrameWnd extends Frame
{
. . .
}
В классе мы определили несколько полей, конструктор для создания окна, метод paint для рисования в окне, метод handleEvent для обработки запроса на уничтожение окна, метод action для обработки события, вызванного кнопкой, расположенной в окне, а также выбором строк меню, созданного для окна.
Класс MessageBox
Для отображения названий выбранных строк меню мы создаем диалоговую панель, определив свой класс MessageBox на базе класса Dialog, как это показано ниже:
class MessageBox extends Dialog
{
. . .
}
В классе MessageBox есть два поля, конструктор, методы handleEvent и action.
Класс SecondPanel
Класс SecondPanel создан также на базе класса Panel:
class SecondPanel extends Panel
{
public void paint(Graphics g)
{
. . .
super.paint(g);
}
}
С использованием этого класса создается нижняя панель. В классе SecondPanel, как и в классе FirstPanel, переопределен метод paint.
Класс String
Класс String предназначен для работы с такими часто встречающимися объектами, как текстовые строки. Методы этого класса позволяют выполнять над строками практически все операции, которые вы делали раньше при помощи библиотечных функций C. Это преобразование строки в число и обратно с любым заданным основанием, определение длины строки, сравнение строк, извлечение подстроки и так далее.
Хотя в языке Java не допускается перезагрузка (переопределение) операторов, для объектов класса Stirng и объектов всех произошедших от него классов сделана встроенная перезагрузка операторов “+” и “+=”. С помощью этих операторов можно выполнять слияние текстовых строк, например:
System.out.println(“x = “ + x + ‘\n’);
Здесь в качестве параметра функции println передается текстовая строка, составленная из трех компонент: строки “x = “, числа x и символа перехода на следующую строку ‘\n’. Значение переменной x автоматически преобразуется в текстовую строку (что выполняется только для текстовых строк) и полученная таким образом текстовая строка сливается со строкой “x = “.
Классы Menu и MenuItem
Для того чтобы дать вам представление о том, что можно делать с меню, приведем краткое описание класса Menu:
public class java.awt.Menu
extends java.awt.MenuItem
implements java.awt.MenuContainer
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание меню с заданным названием
public Menu(String label);
// Создание меню с заданным названием,
// которое может оставаться на экране после того как
// пользователь отпустил клавишу мыши
public Menu(String label, boolean tearOff);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Добавление элемента меню
public MenuItem add(MenuItem mi);
// Добавление строки в меню
public void add(String label);
// Вызов метода createMenu
public void addNotify();
// Добавление разделителя в меню
public void addSeparator();
// Определение количества строк в меню
public int countItems();
// Получение ссылки на элемент меню с заданным номером
public MenuItem getItem(int index);
// Проверка, остается ли меню на экране после того как
// пользователь отпустил клавишу мыши
public boolean isTearOff();
// Удаление заданного элемента меню
public void remove(int index);
// Удаление заданной компоненты меню
public void remove(MenuComponent item);
// Извещение об удалении меню
public void removeNotify();
}
Метод addSeparator используется для добавления в меню разделительной строки. Аналогичный результат достигается и при добавлении в меню стоки “-“:
mnHelp.add("-");
Заметим, что вы можете просто добавлять в меню строки по их названию, пользуясь методом add(String label), либо добавлять в меню элементы класса MenuItem, вызывая метод add(MenuItem mi).
Класс MenuItem определяет поведение отдельных элементов меню:
public class java.awt.MenuItem
extends java.awt.MenuComponent
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public MenuItem(String label);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createMenuItem
public void addNotify();
// Блокирование элемента меню
public void disable();
// Разблокирование элемента меню
public void enable();
// Блокирование или разблокирование элемента меню
public void enable(boolean cond);
// Получение текстовой строки меню
public String getLabel();
// Проверка, является ли элемент меню заблокированным
public boolean isEnabled();
// Получение строки параметров
public String paramString();
// Установка текстовой строки для элемента меню
public void setLabel(String label);
}
Пользуясь методами класса MenuItem вы можете блокировать или разблокировать отдельные строки меню, что нужно делать, например, если в данный момент функция, соответствующая строке меню, недоступна или не определена. Вы также можете изменять текстовые строки, соответствующие элементам меню, что может пригодиться для переопределения их назначения.
Кнопки
Как мы уже говорили, стандартные кнопки создаются на базе класса Button. Этот класс очень простой, поэтому мы приведем его определение полностью:
public class java.awt.Button
extends java.awt.Component
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
public Button();
public Button(String label);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createButton
public void addNotify();
// Получение надписи на кнопке
public String getLabel();
// Получение строки параметров, отражающей
// состоние кнопки
protected String paramString();
// Установка надписи на кнопке
public void setLabel(String label);
}
В классе Button определены два конструктора, первый из которых позволяет создавать кнопку без надписи, а второй - кнопку с надписью. Обычно используется именно второй конструктор.
Из методов класса Button вы будете использовать чаще всего два - getLabel и setLabel. Первый из них позволяет получить строку надписи на кнопке, а второй - установить новую надпись.
Обычно аплет создает в своем окне кнопки в процессе своей инициализации при обработке метода init, например:
Button btn1;
. . .
public void init()
{
btn1 = new Button("Button 1");
add(btn1);
}
Здесь мы создали кнопку с надписью Button 1 и добавили ее в контейнер, которым является окно аплета, с помощью метода add.
Конструктор HelloAp
Конструктор клсса HelloAp находится в одноименном методе и вызывается при создании объекта класса:
public HelloAp()
{
// Сделать: Добавьте сюда код конструктора
}
По умолчанию тело конструктора, создаваемого системой Java Applet Wizard, не содержит никакого кода. Однако вы можете добавить сюда строки, выполняющие инициализацию аплета при его создании как объекта.
Конструктор класса MainFrameWnd
В качестве единственного параметра нашему конструктору передается заголовок создаваемого окна. В первой исполняемой строке наш конструктор вызывает конструктор из базового класса, передавая ему строку заголовка через параметр:
super(sTitle);
Далее конструктор определяет размеры окна, вызывая для него метод resize:
resize(400, 200);
Затем мы устанавливаем для нашего окна желтый цвет фона и черный цвет изображения:
setBackground(Color.yellow);
setForeground(Color.black);
По умолчанию для окон класса Frame устанавливается режим добавления компонент BorderLayout. Мы изменяем этот режим на FlowLayout, вызывая метод setLayout:
setLayout(new FlowLayout());
Установив новый режим добавления компонент, мы располагаем в нашем окне кнопку, предварительно создав ее с помощью конструктора класса Button:
btnOK = new Button("OK");
add(btnOK);
Далее метод init приступает к формированию главного меню окна. Это меню создается как объект класса MenuBar:
mbMainMenuBar = new MenuBar();
Затем мы создаем и наполняем меню “File”:
mnFile = new Menu("File");
mnFile.add("New"); // строка New
mnFile.add("-"); // разделитель
mnFile.add("Exit"); // строка Exit
Это меню создается на базе класса Menu. Обратите внимание, что между строками New и File расположен разделитель.
Аналогичным образом мы добавляем в главное меню другое меню - “Help”:
mnHelp = new Menu("Help");
mnHelp.add("Content"); // строка Content
mnHelp.add("-"); // разделитель
mnHelp.add("About"); // строка About
После своего окончательного формирования меню “File” и “Help” добавляются в главное меню окна mbMainMenuBar:
mbMainMenuBar.add(mnFile);
mbMainMenuBar.add(mnHelp);
И, наконец, когда главное меню будет сформировано, оно подключается к окну вызовом метода setMenuBar, как это показано ниже:
setMenuBar(mbMainMenuBar);
Конструктор класса MessageBox
Наш конструктор создает диалоговую панель с заданным сообщением внутри нее. Ссылка на строку сообщения передается конструктору через первый параметр. Остальные параметры используются конструктором базового класса Dialog для создания диалоговой панели:
super(parent, sTitle, modal);
После вызова конструктора из базового класса наш конструктор устанавливает размеры окна созданной диалоговой панели, вызывая метод resize:
resize(200, 100);
Отменяя установленный по умолчанию режим размещения компонент BorderLayout, конструктор устанавливает режим GridLayout:
setLayout(new GridLayout(2, 1));
Окно диалоговой панели при этом разделяется на две части по горизонтали. В верхнюю часть добавляется текстовое поле для отображения сообщения, в нижнюю - кнопка OK:
lbMsg = new Label(sMsg, Label.CENTER);
add(lbMsg);
btnOK = new Button("OK");
add(btnOK);
Контекст отображения
Способ, которым аплет выполняет рисование в своем окне, полностью отличается от того, которым пользуются программы MS-DOS. Вместо того чтобы обращаться напрямую или через драйвер к регистрам видеоконтроллера, аплет пользуется методами из класса Graphics. Эти методы инкапсулируют все особенности аппаратуры, предоставляя в распоряжение программиста платформно-независимое средство рисования.
Для окна аплета создается объект класса Graphics, ссылка на который передается методу paint. В предыдущей главе мы уже пользовались этим объектом, вызывая для него метод drawString, рисующий в окне текстовую строку. Объект, ссылка на который передается методу paint, и есть контекст отображения. Сейчас мы займемся контекстом отображения вплотную.
Копирование содержимого прямоугольной области
Метод copyArea позволяет скопировать содержимое любой прямоугольной области окна аплета:
public abstract void
copyArea(int x, int y, int width,
int height, int dx, int dy);
Параметры x, y, width и height задают координаты копируемой прямоугольной области. Область копируется в другую прямоугольную область такого же размера, причем параметры dx и dy определяют координаты последней.
Линии
Для того чтобы нарисовать прямую тонкую сплошную линию, вы можете воспользоваться методом drawLine, прототип которого приведен ниже:
public abstract void drawLine(int x1, int y1,
int x2, int y2);
Концы линии имеют координаты (x1, y1) и (x2, y2), как это показано нарис. 3.1.
Рис. 3.1. Рисование прямой линии
К сожалению, в контексте отображения не предусмотерны никакие атрибуты, позволяющие назрисовать пунктирную линию или линию увеличенной толщины.
Литература
1. Фролов А.В., Фролов Г.В. Библиотека системного программиста. М.: ДИАЛОГ-МИФИ
Т.11 - 13. Операционная система Microsoft Windows 3.1 для программиста, 1994
Т.14. Графический интерфейс GDI в Microsoft Windows, 1994
Т.15. Мультимедиа для Windows, 1994
Т.22. Операционная система Windows 95 для программиста, 1996
Т.23. Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets
Т.29. Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI, установка серверов Web для Windows, 1997
2. Д. Родли, Создание Java-апплетов: Пер. с англ., К: НИПФ “ДиаСофт Лтд.”, 1996
3. S. Davic, Learn Lava Now, Microsoft Press, One Microsoft Way, 1996
4. К. Джамса, Java: Пер. с англ., Мн.: ООО “Поппури”, 1996
5. Баженова И.Ю.,Язык программирования Java, М.: ДИАЛОГ-МИФИ, 1997
Массивы в Java
Для создания массива вы можете пользоваться квадратными скобками, расположив их справа от имени массива или от типа объектов, из которых составлен массив, например:
int nNumbers[];
int[] nAnotherNumbers;
Допустимы оба варианта, поэтому вы можете выбрать тот, который вам больше нравится.
При определении массивов в языке Java нельзя указывать их размер. Приведенные выше две строки не вызывают резервирования памяти для массива. Здесь просто создаются ссылки на массивы, которые без инициализации использовать нельзя.
Для того чтобы заказать память для массива, вы должны создать соответствующие объекты с помощью ключевого слова new, например:
int[] nAnotherNumbers;
nAnotherNumbers = new int[15];
Как выполнить инициализацию ячеек таблицы?
Такую инициализацию можно выполнить либо статически, либо динамически. В первом случае вы просто перечисляете значения в фигурных скобках, как это показано ниже:
int[] nColorRed = {255, 255, 100, 0, 10};
Динамическая инициализация выполняется с использованием индекса массива, например, в цикле:
int nInitialValue = 7;
int[] nAnotherNumbers;
nAnotherNumbers = new int[15];
for(int i = 0; i < 15; i++)
{
nAnotherNumbers[i] = nInitialValue;
}
Вы можете создавать массивы не только из переменных базовых типов, но и из произвольных объектов. Каждый элемент такого массива должен инициализироваться оператором new.
Массивы могут быть многомерными и, что интересно, несимметричными.
Ниже создается массив массивов. В нулевом и первом элементе создается массив из четырех чисел, а во втором - из восьми:
int[][] nDim = new int[5][10];
nDim[0] = new int [4];
nDim[1] = new int [4];
nDim[2] = new int [8];
Заметим, что во время выполнения приложения Java виртуальная машина Java проверяет выход за границы массива. Если приложение пытается выйти за границы массива, происходит исключение.
Массивы в языке Java являются объектами некоторого встроенного класса. Для этого класса существует возможность определить размер массива, обратившись к элементу данных класса с именем length, например:
int[] nAnotherNumbers;
nAnotherNumbers = new int[15];
for(int i = 0; i < nAnotherNumbers.length; i++)
{
nAnotherNumbers[i] = nInitialValue;
}
Для определения размера массива вам не нужен такой оператор как sizeof из языка программирования С, так как существует другой способ определения размера массива.
Меню в окне класса Frame
Как мы уже говорили, окно класса Frame может иметь главное меню (Menu Bar) или, как еще говорят, строку меню. Главное меню создается на базе класса MenuBar, краткое описание которого приведено ниже:
public class java.awt.MenuBar
extends java.awt.MenuComponent
implements java.awt.MenuContainer
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public MenuBar();
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Добавление меню в главное меню окна
public Menu add(Menu m);
// Вызов метода createMenuBar
public void addNotify();
// Определение количества меню, добавленных
// в главное меню
public int countMenus();
// Получение ссылки на меню Help
public Menu getHelpMenu();
// Получение ссылки на меню с заданным номером
public Menu getMenu(int i);
// Удаление меню с заданным номером из главного меню
public void remove(int index);
// Удаление компоненты меню
public void remove(MenuComponent m);
// Извещение об удалении меню
public void removeNotify();
// Установка меню Help
public void setHelpMenu(Menu m);
}
Для формирования главного меню окна вы должны создать объект класса MenuBar с помощью конструктора, а затем добавить в него отдельные меню.
Объект главного меню создается следующим образом:
MenuBar mbMainMenuBar;
mbMainMenuBar = new MenuBar();
Отдельные меню создаются на базе класса Menu, например:
Menu mnFile;
Menu mnHelp;
mnFile = new Menu("File");
mnHelp = new Menu("Help");
Создав меню, вы должны добавить в них строки. Для этого нужно вызвать метод add, передав ему в качестве параметра текст строки меню, например:
mnFile.add("New");
mnFile.add("-");
mnFile.add("Exit");
mnHelp.add("Content");
mnHelp.add("-");
mnHelp.add("About");
Далее сформированные меню добавляются в главное меню:
mbMainMenuBar.add(mnFile);
mbMainMenuBar.add(mnHelp);
И, наконец, теперь можно устанавливать главное меню в окне класса, созданного на базе класса Frame:
setMenuBar(mbMainMenuBar);
Метод action класса FrameWnd
Назначение метода action класса FrameWnd - обработка событий, вызванных кнопками кнопку “Show Frame Window” и “Hide Frame Window”, созданных в окне аплета:
if(evt.target.equals(btnShowFrame))
fMainFrame.show();
else if(evt.target.equals(btnHideFrame))
fMainFrame.hide();
Если нажата кнопка “Show Frame Window”, для окна fMainFrame вызывается метод show, что приводит к появлению окна на экране. Если нажата кнопка “Hide Frame Window”, для этого окна вызывается метод hide, после чего окно исчезает с экрана. Исчезнувшее окно не уничтожается и вы можете снова отобразить его на экране, нажав кнопку “Show Frame Window”.
Метод action класса MainFrameWnd
Этот метод обрабатывает события, связанные с кнопкой и меню.
Если пользователь нажимает на кнопку OK, расположенную в окне, окно скрывается методом hide:
if(evt.target.equals(btnOK))
{
hide();
}
Здесь для вас нет ничего нового.
Рассмотрим более подробно процедуру обработки событий от меню.
Вначале метод action проверяет, вызвано ли событие выбором строки из меню, сравнивая объект события с классом MenuItem:
else if(evt.target instanceof MenuItem)
{
. . .
}
else
return false;
Если это так, в поле mnItem сохраняется ссылка на элемент меню, вызвавший событие:
mnItem = (MenuItem)evt.target;
Однако мы не используем этот элемент, так как для определения строки, выбранной пользователем, нам достаточно проанализировать второй параметр метода action:
if(obj.equals("Exit"))
System.exit(0);
else if(obj.equals("New"))
{
MessageBox mbox;
mbox = new MessageBox("Item New selected",
this, "Dialog from Frame", true);
mbox.show();
}
else if(obj.equals("Content"))
{
. . .
}
else if(obj.equals("About"))
{
. . .
}
В данном случае второй параметр метода action будет представлять собой ссылку на строку, выбранную из меню, поэтому для определения выбранной строки мы можем выполнить простое сравнение методом equals.
Если пользователь выбрал из меню File строку Exit, мы вызываем метод System.exit, предназначенный для завершения работы виртуальной машины Java. Таким способом вы можете завершить работу аплета, когда он выполняется в среде Microsoft Visual J++ в процессе отладки. Если же аплет запущен автономно в навигаторе, то завершения работы навигатора не произойдет.
В том случае когда пользователь выбирает любую другую строку из меню, метод action создает диалоговую панель на базе определенного нами класса MessageBox. В этой диалоговой панели отображаетя название выбранной строки меню.
Заметим, что сразу после создания конструктором диалоговая панель не появляется на экране. Мы отображаем ее, вызывая метод show.
Метод action класса MessageBox
Если пользователь нажимает кнопку OK, расположенную в окне диалоговой панели, метод action вызывает для панели метод dispose, удаляя эту панель с экрана и из памяти:
if(evt.target.equals(btnOK))
{
dispose();
}
Метод action
Метод action проверяет, является ли объект, создавший событие, кнопкой. Для этого он сравнивает ссылку на объект, передаваемую через поле evt.target, с объектом Button, пользуясь оператором instanceof. Так как поле evt.target может содержать ссылку на любой объект, способный создавать события, а не только на объект типа Button, эта проверка необходима для исключения ложных срабатываний на чужие события.
Если событие создано кнопкой, ссылка на эту кнопку сохраняется в переменной btn:
Button btn;
btn = (Button)evt.target;
При этом мы выполняем преобразование типов.
Далее метод action получает название кнопки (то есть строку, написанную на поверхности кнопки) и сохраняет его в переменной sTextLabel:
sTextLabel = btn.getLabel();
Для получения строки названия кнопки используется метод getLabel, определенный в классе Button.
Затем метод action проверяет, от какой конкретно кнопки пришло событие, выполняя ветвление с помощью оператора if - else if - else:
if(evt.target.equals(btn1))
{
showStatus("Button 1 (\"" + sTextLabel + "\") pressed");
}
else if(evt.target.equals(btn2))
{
showStatus("Button 2 (\"" + sTextLabel + "\") pressed");
}
. . .
else
{
return false;
}
Название нажатой кнопки отображается в строке состояния навигатора. Если событие создано кнопкой, обработчик которой не предусмотрен в нашем методе action, метод просто возвращает значение false, отказываясь таким образом от обработки события.
Если ваша реализация метода action не обрабатывает событие, она может передать его методу action базового класса, как это показано ниже:
super.action(evt, obj);
В том случае, когда событие было обработано, метод action перерисовывает окно аплета, вызывая метод repaint, и затем возвращает значение true:
repaint();
return true;
Метод action обрабатывает только те события, которые вызваны кнопкой btnGet:
if(evt.target instanceof Button)
{
if(evt.target.equals(btnGet))
showStatus("Button 1 pressed");
else
return false;
repaint();
return true;
}
Когда пользователь нажимает кнопку, метод action выводит сообщение об этом в строку состояния навигатора и перерисывавает окно аплета. Текущее состояние кнопок определяется методом paint во время перерисовки окна.
Метод action обрабатывает событие, вызванное списками, - выбор элемента из списка.
Прежде всего, метод action сохраняет ссылку на список, в котором произошел выбор, в переменной ch:
Choice ch;
ch = (Choice)evt.target;
Далее выполняется проверка факта, что событие вызванно именно списком, после чего происходит анализ, в каком именно списке сделан выбор нового элемента:
if(evt.target.equals(chBackgroundColor))
{
if(ch.getSelectedIndex() == 0)
setBackground(Color.yellow);
else if(ch.getSelectedIndex() == 1)
setBackground(Color.green);
else if(ch.getSelectedIndex() == 2)
setBackground(Color.white);
}
else if(evt.target.equals(chForegroundColor))
{
if(ch.getSelectedIndex() == 0)
setForeground(Color.black);
else if(ch.getSelectedIndex() == 1)
setForeground(Color.red);
else if(ch.getSelectedIndex() == 2)
setForeground(Color.blue);
}
else
return false;
Обратите внимание, что мы вначале выполняем преобразование типа evt.target к классу Choice, а только затем проверяем, действительно ли событие вызвано списком. Правильно ли это?
Вообще говоря, неправильно. Так как в поле evt.target могут находиться ссылки на объекты различных классов, в процессе выполнения преобразования типов может произойти исключение. Если предпринимается попытка выполнить преобразование для несовместимых типов. Но так как в нашем аплете события создаются только списками, исключение не возникает.
Корректнее было бы вначале проверить ссылку evt.target на принаддлежность к классу Choice с помощью оператора instanceof, а только потом выполнять преобразование типов. Так мы и будем делать в следующих примерах аплетов, обрабатывающих события от различных источников.
С помощью метода getSelectedIndex метод action определяет номер выбранного элемента списка, устанавливая соответствующим образом цвет фона или изображения.
Метод action обрабатывает событие, вызванное списками, - выбор элемента из списка.
Прежде всего, метод action сохраняет ссылку на список, в котором произошел выбор, в переменной ch:
List ch;
ch = (List)evt.target;
Далее выполняется проверка факта, что событие вызванно именно списоком класса List, а затем обрабатываются события, созданные списками:
if(evt.target.equals(chBackgroundColor))
{
if(ch.getSelectedIndex() == 0)
setBackground(Color.yellow);
else if(ch.getSelectedIndex() == 1)
setBackground(Color.green);
else if(ch.getSelectedIndex() == 2)
setBackground(Color.white);
else if(ch.getSelectedIndex() == 3)
setBackground(Color.black);
else if(ch.getSelectedIndex() == 4)
setBackground(Color.red);
else if(ch.getSelectedIndex() == 5)
setBackground(Color.blue);
}
else if(evt.target.equals(chForegroundColor))
{
if(ch.getSelectedIndex() == 0)
setForeground(Color.black);
else if(ch.getSelectedIndex() == 1)
setForeground(Color.red);
else if(ch.getSelectedIndex() == 2)
setForeground(Color.blue);
else if(ch.getSelectedIndex() == 3)
setForeground(Color.yellow);
else if(ch.getSelectedIndex() == 4)
setForeground(Color.green);
else if(ch.getSelectedIndex() == 5)
setForeground(Color.white);
}
else
return false;
С помощью метода getSelectedIndex метод action определяет номер выбранного элемента списка, устанавливая соответствующим образом цвет фона или изображения. Затем метод перерисовывает окно аплета, вызывая метод repaint.
Наш метод action обрабатывает только те события, которые вызваны кнопками. Проверка источника события выполняется так же, как и раньше, поэтому мы не будем на этом останавливаться.
Что же касается установки выравнивания, то она выполняется при помощи метода setAlignment:
if(evt.target.equals(btnLeft))
lbTextLabel.setAlignment(Label.LEFT);
else if(evt.target.equals(btnCenter))
lbTextLabel.setAlignment(Label.CENTER);
else if(evt.target.equals(btnRight))
lbTextLabel.setAlignment(Label.RIGHT);
else
return false;
Наш метод action обрабатывает только те события, которые вызваны кнопкой.
Обработка заключается в извлечении текста из поля редактирования и записи его в строку str:
str = txt.getText();
repaint();
Наш метод action обрабатывает только те события, которые вызваны кнопками.
Обработка заключается в извлечении текста из поля редактирования и записи его в строку str. В зависимости от того, какая кнопка была нажата, извлекается либо весь текст, либо только выделенный фрагмент:
if(evt.target.equals(btnGetText))
{
str = txt.getText();
repaint();
}
else if(evt.target.equals(btnGetSelectedText))
{
str = txt.getSelectedText();
repaint();
}
else
return false;
Для извлечения всего текста мы вызываем метод getText, а для извлечения выделенного фрагмента - метод getSelectedText.
После записи извлеченного текста метод action перерисовывает окно аплета, вызывая метод repaint.
Метод action обрабатывает события, связанные с кнопками, расположенными в обеих панелях.
Вначале метод проверяет, что событие создано кнопкой. Далее идентификатор кнопки, вызвавшей событие, записывается в переменную btn:
btn = (Button)evt.target;
После этого метод action получает название кнопки, сохраняет его в строке sTextLabel, а затем отображает в строке состояния навигатора:
sTextLabel = btn.getLabel();
showStatus("Button (\"" + sTextLabel + "\") pressed");
Далее анализируется ссылка на кнопку. Если была нажата одна из управляющих кнопок, происходит перелистывание страниц блокнота, в прямом или обратном направлении:
if(evt.target.equals(btnNext))
{
((CardLayout)pCardPanel.getLayout()).next(pCardPanel);
}
else if(evt.target.equals(btnPrev))
{
((CardLayout)pCardPanel.getLayout()).previous(pCardPanel);
}
Здесь мы с помощью метода getLayout получаем ссылку на интерфейс системы Layout Manager, установленной для панели pCardPanel, а затем, пользуясь полученной ссылкой, вызываем методы next или previous. Обратите также внимание на необходимость явного приведения типа к классу CardLayout, в котором определены указанные методы.
Обработка событий, создаваемых кнопками, которые расположены на страницах блокнота, не имеет никаких особенностей:
else if(evt.target.equals(btn1))
{
showStatus("Button 1 (\"" + sTextLabel
+ "\") pressed");
}
. . .
else if(evt.target.equals(btn4))
{
showStatus("Button 4 (\"" + sTextLabel
+ "\") pressed");
}
Название кнопки просто отображается в строке состояния навигатора.
Метод action выполняет раздельную обработку событий, вызванных кнопками и списками.
Если событие было вызвано кнопками, выполняется переключение страниц блокнота:
if(evt.target.equals(btnNext))
((CardLayout)pCardPanel.getLayout()).next(pCardPanel);
else if(evt.target.equals(btnPrev))
((CardLayout)pCardPanel.getLayout()).previous(pCardPanel);
else if(evt.target.equals(btnBackgroundColor))
((CardLayout)pCardPanel.getLayout()).show(
pCardPanel, "BackgroundColor");
else if(evt.target.equals(btnForegroundColor))
((CardLayout)pCardPanel.getLayout()).show(
pCardPanel, "ForegroundColor");
else if(evt.target.equals(btnFont))
((CardLayout)pCardPanel.getLayout()).show(
pCardPanel, "Font");
Для выбора следующей и предыдущей страницы здесь использованы методы next и previous.
Выбор конкретной страницы по ее имени осуществляется с помощью метода show. В качестве параметров этому методу передается ссылка на панель блокнота и имя страницы.
Обратите также внимание на способ обработки событий, не имеющих отношения к нашим компонентам:
return super.action(evt, obj);
Здесь мы вызываем метод action из базового класса, который после соответствующей обработки события вернет значение true или false.
Если событие вызвано кнопками управления блокнотом, мы перерисовываем окно верхней панели, окно всего аплета и затем возвращаем признак успешной обработки события:
pDraw.repaint();
repaint();
return true;
Как вы увидите дальше, в процессе перерисовки окна всего аплета метод paint выполнит рисование в окне верхней панели.
События, связанные с выбором нового цвета фона и изображения обрабатываются следующим образом:
if(evt.target.equals(chBackgroundColor))
{
if(ch.getSelectedIndex() == 0)
pDraw.setBackground(Color.yellow);
else if(ch.getSelectedIndex() == 1)
pDraw.setBackground(Color.green);
else if(ch.getSelectedIndex() == 2)
pDraw.setBackground(Color.white);
}
else if(evt.target.equals(chForegroundColor))
Метод destroy класса FrameWnd
При завершении работы аплета мы удаляем созданное нами окно и освобождаем все связанные с ним ресурсы, вызывая для окна метод dispose:
fMainFrame.dispose();
Метод destroy
Перед удалением аплета из памяти вызывается метод destroy, который определен в базовом классе Applet как пустая заглушка. Система Java Applet Wizard добавляет в исходный текст класса переопределение метода destroy, которое выглядит следующим образом:
public void destroy()
{
// Сделать: Добавьте сюда код завершения работы аплета
}
Здесь вы можете выполнить все необходимые операции, которые следует выполнить перед удалением аплета. Например, если в методе init вы создавали какие-либо задачи, в методе destroy вы можете их завершить.
Метод getAppletInfo класса FrameWnd
Этот метод возвращает информацию об аплете FrameWnd.
Метод getAppletInfo возвращает информацию об
Метод getAppletInfo возвращает информацию об аплете.
Метод getAppletInfo
Базовый класс Applet содержит определение метода getAppletInfo, возвращающее значение null. В нашем классе HelloAp, который является дочерним по отношению к классу Applet, система Java Applet Wizard переопределила метод getAppletInfo из базового класса следующим образом:
public String getAppletInfo()
{
return "Name: HelloAp\r\n" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
Теперь этот метод возвращает текстовую информацию об аплете в виде объекта класса String.
Заметьте, что здесь возвращается достаточно длинная строка. Она разделена на три части, но способ разделения отличен от принятого в языке программирования С: части строки объединены оператором “+”. Для объектов класса String этот оператор в языке Java переопределен и имеет очевидное назначение - слияние строк.
В методе getAppletInfo изменения невелики: в описание аплета был добавлен наш почтовый адрес E-mail и адрес нашего сервера WWW:
public String getAppletInfo()
{
return "Name: Painter\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
Метод getAppletInfo ничем не отличается от аналогичных методов в предыдущих приложениях.
Метод getAppletInfo возвращает информацию об аплете и не имеет никаких особенностей.
Метод getAppletInfo возвращает информацию об аплете и не имеет никаких особенностей.
Метод getAppletInfo, возвращающий строку информации об аплете, не имеет никаких особенностей.
Метод getAppletInfo возвращает информацию о нашем аплете.
Метод getAppletInfo возвращает информацию об аплете ChoiceList.
Метод getAppletInfo возвращает информацию об аплете ListBox.
Метод getAppletInfo возвращает информацию о нашем аплете.
Метод getAppletInfo возвращает информацию о нашем аплете.
Метод getAppletInfo возвращает информацию о нашем аплете.
Метод getAppletInfo возвращает информацию об аплете и не имеет никаких особенностей.
Метод getAppletInfo возвращает информацию об аплете.
Метод getParameterInfo
Система Java Applet Wizard переопределяет метод getParameterInfo, который возвращает ссылку на массив массивов с описаниями параметров:
public String[][] getParameterInfo()
{
String[][] info =
{
{ PARAM_Str1, "String", "Text string to write" },
. . .
{ PARAM_Str5, "String", "Text string to write" },
{ PARAM_Font1, "String", "Text font" },
. . .
{ PARAM_Font5, "String", "Text font" },
{ PARAM_Type1, "String", "Font type" },
{ PARAM_Type2, "String", "Font type" },
{ PARAM_Type3, "String", "Font type" },
};
return info;
}
Как мы уже говорили, эта информация может использоваться другими аплетами, размещенными в том же документе HTML и работающими одновременно с нашим аплетом.
Метод handleEvent класса MainFrameWnd
Для того чтобы определить реакцию окна на попытку пользователя закрыть окно с помощью органов управления, расположенных в заголовке окна, или другим способом, мы переопределили метод handleEvent.
При получении кода события Event.WINDOW_DESTROY (удаление окна) мы просто скрываем окно, вызывая метод hide:
if(evt.id == Event.WINDOW_DESTROY)
{
hide();
return true;
}
else
return super.handleEvent(evt);
Все другие события передаются для обработки методу handleEvent из базового класса.
Метод handleEvent класса MessageBox
Когда пользователь пытается закрыть окно диалоговой панели, например, сделав двойной щелчок левой клавишей мыши по системному меню или одиночный щелчок по кнопке удаления окна, возникает событие Event.WINDOW_DESTROY. Мы его обрабатываем следующим образом:
if(evt.id == Event.WINDOW_DESTROY)
{
dispose();
return true;
}
else
return super.handleEvent(evt);
Вызывая метод dispose, мы удаляем окно диалоговой панели и освобождаем все связанные с ним ресурсы.
Метод handleEvent
Для того чтобы отследить выделение элементов списка, наш аплет переопределил метод handleEvent, обеспечив обработку события с идентификатором Event.LIST_SELECT.
Переопределение метода handleEvent нужно делать внимательно, так как этот метод вызывается при возникновении разных событий, например, при перемещении мыши в окне аплета. Если ваш метод handleEvent не обрабатывает какое-либо событие, он должен передать его одноименному методу из базового класса.
Наш метод handleEvent прежде всего проверяет код события, обрабатывая только события Event.LIST_SELECT, которые создаются при выделении пользователем элементов списка:
if(evt.id == Event.LIST_SELECT)
{
. . .
}
else
return super.handleEvent(evt);
Если событие подлежит обработке, наш метод handleEvent получает ссылку на объект, вызвавший событие, и сохраняет ее в переменной ls типа List:
List ls;
ls = (List)evt.target;
Затем метод определяет, какой сисок создал событие, проверяя поле evt.target, а затем получает и записывает выделенную строку в переменную sSelBackground (для списка цветов фона) или sSelForeground (для списка цветов изображения):
if(evt.target.equals(chBackgroundColor))
sSelBackground = ls.getSelectedItem();
else if(evt.target.equals(chForegroundColor))
sSelForeground = ls.getSelectedItem();
После этого цвет фона и изображения записывается в строку состояния навигатора в формате (<цвет фона>, <цвет изображения>):
showStatus("(" + sSelBackground + ",
" + sSelForeground + ")");
После этого метод выполняет перерисовку окна и возвращает значение true - признак того, что он обработал событие:
repaint();
return true;
Метод init класса FrameWnd
В процессе инициализации аплета метод init создает объект класса MainFrameWnd - перекрывающееся окно с заголовком "Main Frame Window":
fMainFrame = new MainFrameWnd("Main Frame Window");
Для этого вызывается конструктор из класса MainFrameWnd, созданного нами на базе класса Frame.
После этого метод init создает две кнопки и добавляет их в окно аплета:
btnShowFrame = new Button("Show Frame Window");
add(btnShowFrame);
btnHideFrame = new Button("Hide Frame Window");
add(btnHideFrame);
Первая из этих кнопок предназначена для отображения перекрывающегося окна, а вторая - для временного удаления или скрытия этого окна.
Прежде всего метод init устанавливает для окна панели режим добавления компонент GridLayout, определяя таблицу из двух строк и одного столбца.
Первая панель создается на базе класса FirstPanel, определенного в нашем приложении:
pPanel1 = new FirstPanel();
Этот класс мы рассмотрим позже.
Созданная панель добавляется в окно аплета методом add:
add(pPanel1);
Аналогично мы создаем и вторую панель, на этот раз как объект класса SecondPanel:
pPanel2 = new SecondPanel();
Вторая панель добавляется в окно аплета точно также, как и первая:
add(pPanel2);
Для того чтобы выделить панели на фоне окна аплета, мы устанавливаем для них разные цвета фона и изображения. Для первой панели устанавливается желтый цвет фона и черный цвет изображения:
pPanel1.setBackground(Color.yellow);
pPanel1.setForeground(Color.black);
Для второй панели мы устанавливаем белый цвет фона и красный цвет изображения:
pPanel2.setBackground(Color.white);
pPanel2.setForeground(Color.red);
В результате цвета рамки окна и текста первой и второй панели будут разными.
На последнем этапе инициализации мы инициируем вызов метода paint класса Panel2, вызывая для этого метод repaint:
repaint();
Это нужно для того, чтобы сразу после отображения окна аплета выполнить рисование текстовых строк внутри окон панелей.
Метод init
Метод init, так же как и метод getAppletInfo, определен в базовом классе Applet, от которого наследуются все аплеты. Определение его таково, что этот метод ровным счетом ничего не делает.
Когда вызывается метод init и зачем он нужен?
Метод init вызывается тогда, когда навигатор Internet загружает в свое окно документ HTML с оператором , ссылающимся на данный аплет. В этот момент аплет может выполнять инициализацию, например, создавать задачи, если он работает в мультизадачном режиме.
Существует контрпара для метода init - метод destroy. О нем мы расскажем немного позже.
Система Java Applet Wizard переопределяет метод init следующим образом:
public void init()
{
resize(320, 240);
// Сделать: Добавьте сюда дополнительный код
// инициализации
}
Здесь вызывается метод resize, который изменяет размер окна аплета. Этот метод определен в базовом классе Applet. В нашем классе вы можете вызывать его потому, что мы образовали этот класс от класса Applet.
Забегая вперед, скажем, что параметры оператора , с помощью которого аплет встраивается в документ HTML, допускают установку размеров окна аплета. Пользуясь методом resize, вы можете изменить эти размеры.
Если же вы желаете изменять размеры окна, редактируя параметры оператора в документе HTML, вы должны удалить вызов метода resize из исходного текста метода init.
Аплет Painter был создан с помощью системы Java Applet Wizard. Мы выполнили изменения методов init, getAppletInfo и paint.
Изменения метода init заключаются в том, что мы закрыли символом комментария строку установки размеров окна аплета:
public void init()
{
// resize(320, 240);
}
Это позволяет задавать размеры окна аплета не в исходном тексте приложения, а в параметрах оператора , с помощью которого аплет встраивается в документ HTML.
Процедура извлечения списка доступных шрифтов, использованная в нашем аплете, достаточно проста и выполняется в методе init, как это показано ниже:
Toolkit toolkit; // ссылка на Toolkit
String fntlist[]; // список шрифтов
. . .
public void init()
{
toolkit = Toolkit.getDefaultToolkit();
fntlist = toolkit.getFontList();
}
Аплет вызывает статический метод getDefaultToolkit из класса Toolkit и затем, пользуясь полученной ссылкой, извлекает список шрифтов, записывая его в массив fntlist.
Для чего еще можно использовать класс Toolkit?
Класс Toolkit является абстрактным суперклассом для всех реализаций AWT. Порожденные от него классы используются для привязки различных компонент конкретных реализаций.
Создавая свои аплеты, вы будете редко прибегать к услугам этого класса. Однако в нем есть несколько полезных методов, прототипы которых мы перечислим ниже:
public abstract class java.awt.Toolkit
extends java.lang.Object
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public Toolkit();
// -----------------------------------------------------
// Методы (сокращенный список)
// -----------------------------------------------------
. . .
// Получение ссылки на Toolkit
public static Toolkit getDefaultToolkit();
// Определение текущей цветовой модели,
// выбранной в контекст отображения
public abstract ColorModel getColorModel();
// Получение списка шрифтов, доступных аплету
public abstract String[] getFontList();
// Получение метрик заданного шрифта
public abstract FontMetrics getFontMetrics(Font font);
// Получение растрового изображения по имени файла
public abstract Image getImage(String filename);
// Получение растрового изображения по адресу URL
public abstract Image getImage(URL url);
// Определение разрешения экрана в точках на дюйм
public abstract int getScreenResolution();
// Размеры экрана в пикселах
public abstract Dimension getScreenSize();
// Подготовка растрового изображения для вывода
public abstract boolean
prepareImage(Image image, int width, int height,
ImageObserver observer);
// Синхронизация состояния Toolkit
public abstract void sync();
}
Наиболее интересны, с нашей точки зрения, методы getFontList, getScreenResolution и getScreenSize, с помощью которых аплет может, соответственно, плучить список шрифтов, определить разрешение и размер экрана. Последние два параметра позволяют сформировать содержимое окна аплета оптимальным образом исходя из объема информации, который может в нем разместиться.
При инициализации аплета метод init читает все параметры и записывает их значения в соответствующие поля класса, как это показано ниже:
public void init()
{
String param;
param = getParameter(PARAM_Str1);
if (param != null)
m_Str1 = param;
. . .
param = getParameter(PARAM_Str5);
if (param != null)
m_Str5 = param;
// Шрифты для отображения строк
param = getParameter(PARAM_Font1);
if (param != null)
m_Font1 = param;
. . .
param = getParameter(PARAM_Font5);
if (param != null)
m_Font5 = param;
// Начертание шрифтов
param = getParameter(PARAM_Type1);
if (param != null)
m_Type1 = param;
. . .
param = getParameter(PARAM_Type3);
if (param != null)
m_Type3 = param;
}
Здесь все просто. Метод init по очереди получает значения параметров методом getParameter, которому в качестве параметра передается имя параметра аплета. Полученное значение сохраняется в рабочей переменной param и, если оно отлично от значения null, сохраняется в соответствующем поле класса.
Метод init сбрасывает признак рисования, записывая в поле bDrawing значение false, а также создает новый динамический массив в виде объекта класса Vector:
public void init()
{
bDrawing = false;
lines = new Vector();
}
Этот метод получает контекст отображения, однако не для рисования, а для определения метрик шрифта:
Graphics g = getGraphics();
FontMetrics fm = g.getFontMetrics();
В переменную yHeight заносится полная высота символов текущего шрифта:
yHeight = fm.getHeight();
При инициализации аплета метод init создает четыре кнопки, сохраняя ссылки на них в соответствующих полях нашего класса, а также добавляет эти кнопки в окно аплета, вызывая для этого метод add:
public void init()
{
btn1 = new Button("Button 1");
btn2 = new Button("Button 2");
btn3 = new Button("Button 3");
btn4 = new Button("Button 4");
add(btn1);
add(btn2);
add(btn3);
add(btn4);
sTextLabel = new String("");
}
После добавления кнопок в строку sTextLabel записывается пустое значение, так как ни одна кнопка еще не была нажата.
До сих пор для изменения цвета фона окна наших аплетов мы раскрашивали окно в желтый цвет явным образом в методе paint. Однако есть и другой способ, основанный на вызове метода setBackground:
setBackground(Color.yellow);
Дополнением к этому методу может послужить метод setForeground, с помощью которого можно установить цвет для рисования в окне.
Почему мы выбрали другой способ изменения фона окна?
Дело в том, что переключатели сами по себе являются окнами, обладающими такими атрибутами, как цвет фона и цвет изображения. Если просто нарисовать их в окне, закрашенным желтым цветом, то получится, что серые переключатели нарисованы на желтом фоне, что некрасиво. Метод setBackground, вызванный для окна аплета, позволяет задать цвет фона не только для контейнера, но и для всех компонент, расположенных в нем.
После установки цвета фона метод init создает три переключателя с независимой фиксацией, указывая их название:
chbox1 = new Checkbox("Switch 1");
chbox2 = new Checkbox("Switch 2");
chbox3 = new Checkbox("Switch 3");
Далее метод init создает группу переключателей с зависимой фиксацией в виде объекта класса CheckboxGroup:
grModeGroup = new CheckboxGroup();
Для создания переключателей с зависимой фиксацией необходимо использовать метод, допускающий указание группы и начального состояния переключателя:
rdbox1 = new Checkbox("Mode 1",grModeGroup, true);
rdbox2 = new Checkbox("Mode 2",grModeGroup, false);
rdbox3 = new Checkbox("Mode 3",grModeGroup, false);
rdbox4 = new Checkbox("Mode 4",grModeGroup, false);
Затем метод init создает кнопку с названием Get CheckBoxes state, предназначенную для определения текущего состояния переключателей:
btnGet = new Button("Get CheckBoxes state");
После создания компонент они добавляются в контейнер, которым является окно аплета. Для этого используется метод add.
Прежде всего мы добавляем четыре переключателя с зависимой фиксацией:
add(rdbox1);
add(rdbox2);
add(rdbox3);
add(rdbox4);
Размеры окна и размеры переключателей соотносятся между собой таким образом, что в верхней части окна аплета помещаются как раз четыре переключателя. Очевидно, если изменить размеры окна аплета, переключатели будут размещены по-другому.
Далее метод init добавляет в окно аплета переключатели с независимой фиксацией и кнопку:
add(chbox1);
add(chbox2);
add(chbox3);
add(btnGet);
В методе init мы создаем два списка как объекты класса Choice:
chBackgroundColor = new Choice();
chForegroundColor = new Choice();
Созданные списки пока пустые, но мы можем добавить их в окно аплета, вызвав метод add:
add(chBackgroundColor);
add(chForegroundColor);
Сразу после добавления списков мы их заполняем, вызывая для соответствующих объектов метод addItem:
chBackgroundColor.addItem("Yellow");
chBackgroundColor.addItem("Green");
chBackgroundColor.addItem("White");
chForegroundColor.addItem("Black");
chForegroundColor.addItem("Red");
chForegroundColor.addItem("Blue");
Элементы, добавленные в список первыми, будут выбраны в списке по умолчанию. В нашем случае будет выбран фон желтого цвета и изображение черного цвета.
Такие же цвета мы устанавливаем для окна аплета, вызывая методы setBackground и setForeground:
setBackground(Color.yellow);
setForeground(Color.black);
В методе init мы создаем два списка как объекты класса List:
chBackgroundColor = new List(6, false);
chForegroundColor = new List(4, false);
Первый из этих списков способен одновременно отображать шесть строк, поэтому в нем поместились все шесть цветов для фона. Вертикальный размер второго списка меньше. В результате он снабжается полосой просмотра. Оба списка не предназначены для одновременного выбора нескольких элементов, поэтому в качетсве второго параметра мы передаем конструктору List значение false.
Созданные списки добавляются в окно аплета методом add:
add(chBackgroundColor);
add(chForegroundColor);
Сразу после добавления списков мы их заполняем, вызывая для соответствующих объектов метод addItem:
chBackgroundColor.addItem("Yellow");
chBackgroundColor.addItem("Green");
chBackgroundColor.addItem("White");
chBackgroundColor.addItem("Black");
chBackgroundColor.addItem("Red");
chBackgroundColor.addItem("Blue");
chForegroundColor.addItem("Black");
chForegroundColor.addItem("Red");
chForegroundColor.addItem("Blue");
chForegroundColor.addItem("Yellow");
chForegroundColor.addItem("Green");
chForegroundColor.addItem("White");
Затем метод выбирает для фона желтый цвет, а для изображения - черный:
setBackground(Color.yellow);
setForeground(Color.black);
Метод init создает одно текстовое поле, вызывая конструктор с одним параметром - текстовой строкой:
lbTextLabel = new Label("Выберите выравнивание");
Далее этот метод создает три кнопки, с помощью которых вы будете изменять выравнивание текста в поле класса Label:
btnLeft = new Button("Влево");
btnCenter = new Button("Центровка");
btnRight = new Button("Вправо");
Затем созданные кнопки и поле добавляются в окно аплета при помощи метода add:
add(btnLeft);
add(btnCenter);
add(btnRight);
add(lbTextLabel);
Последнее, что делает метод init перед возвращением управления, это изменение цвета фона:
setBackground(Color.yellow);
Метод init создает одно текстовое поле редактирования, вызывая конструктор с параметром в виде текстовой строки:
txt = new TextField("Введите строку текста", 35);
Далее этот метод создает кнопку, с помощью которой можно получить текущее содержимое поля редактирования:
btnGetText = new Button("Получить строку");
Затем созданные поле и кнопка добавляются в окно аплета при помощи метода add:
add(txt);
add(btnGetText);
После этого метод init получает текущее содержимое поля редактирования и записывает его в строку str:
str = txt.getText();
В завершении метод init изменяет цвет фона:
setBackground(Color.yellow);
Метод init создает одно текстовое поле редактирования, вызывая конструктор следующего вида:
txt = new TextArea("Введите строку текста", 5, 35);
Здесь создается поле из 5 строк и 35 столбцов.
Далее этот метод создает кнопки, с помощью которых можно получить текущее содержимое всего поля редактирования и области, выделенной пользователем:
btnGetText = new Button("Получить все");
btnGetSelectedText = new Button("Получить выделенное");
Затем созданные поле и кнопки добавляются в окно аплета при помощи метода add:
add(txt);
add(btnGetText);
add(btnGetSelectedText);
После этого метод init получает текущее содержимое поля редактирования и записывает его в строку str, а затем изменяет цвет фона:
str = txt.getText();
setBackground(Color.yellow);
Метод init создает все необходимые панели и добавляет в них компоненты.
В самом начале своей работы метод init устанавливает желтый цвет фона для окна аплета:
setBackground(Color.yellow);
После этого выбирается такой режим размещения компонентов, при котором они добавляются в таблицу, состоящую из двух строк и одного столбца:
setLayout(new GridLayout(2, 1));
Мы будем добавлять панели, поэтому первая из добавленных панелей окажется в верхней строке этой таблицы и займет верхнюю половину окна аплета, а вторая - нижнюю.
Далее метод init создает пять кнопок, которые будут добавлены на страницы блокнота, расположенного в верхней панели:
btn1 = new Button("Button 1");
btn2 = new Button("Button 2");
btn3 = new Button("Button 3");
btn4 = new Button("Button 4");
btn5 = new Button("Button 5");
После создания кнопок метод init приступает к созданию и заполнению панели блокнота. Панель создается при помощи конструктора класса Panel:
pCardPanel = new Panel();
Для этой панели устанавливается режим размещения компонент типа CardLayout, причем между границами окна панели и границами окна добавляемых компонент по вертикали и горизонтали оставлен зазор 5 пикселов:
pCardPanel.setLayout(new CardLayout(5, 5));
После установки режима добавления можно заполнять блокнот кнопками:
pCardPanel.add(btn1);
pCardPanel.add(btn2);
pCardPanel.add(btn3);
pCardPanel.add(btn4);
pCardPanel.add(btn5);
Здесь мы воспользовались известным вам методом add, вызвав его для объекта pCardPanel.
Заполненная панель блокнота при помощи все того же метода add добавляется в окно аплета:
add(pCardPanel);
Так как эта панель добавляется в окно аплета первой, она займет верхнюю половину этого окна.
Завершив с панелью блокнота, метод init приступает к формированию панели управляющих кнопок.
Вначале метод создает сами управляющие кнопки:
btnNext = new Button("Next");
btnPrev = new Button("Prev");
Первая из них перелистывает страницы блокнота в прямом направлении, а вторая - в обратном.
Затем создается панель кнопок:
pButtonPanel = new Panel();
Для панели кнопок мы выбираем режим выравнивания FlowLayout, при котором компоненты добавляются слева направо и сверху вниз:
pButtonPanel.setLayout(new FlowLayout());
После этого в панель добавляются две управляющие кнопки, преднаазначенные для перелистывания страниц блокнота:
pButtonPanel.add(btnNext);
pButtonPanel.add(btnPrev);
На завершающем этапе своей работы метод init добавляет панель управляющих кнопок в окно аплета:
add(pButtonPanel);
Данная панель добавляется в окно аплета второй по счету, поэтому она будет расположена в нижней половине этого окна.
Метод init выполняет достаточно громоздкую работу по созданию и добавлению различных панелей и других компонентов. К сожалению, приложениям Java не доступны ресурсы, аналогичные ресурсам операционной системы Microsoft Windows, поэтому формирование диалоговых панелей и других элементов пользовательского интерфейса приходится выполнять чисто программными методами на этапе выполнения приложения. Средства среды разработки приложений Java Microsoft Visual J++ версии 1.1, о которых мы уже упоминали, позволяют несколько упростить этот процесс.
Свою работу метод init начинает с установки желтого цвета фона для окна аплета.
Далее устанавливается режим добавления GridLayout, разделяющий окно аплета на три части по горизонтали:
setLayout(new GridLayout(3, 1));
Соответствующая таблица, в которую будут добавляться компоненты, имеет три строки и один столбец.
Панель блокнота создается следующим образом:
pCardPanel = new Panel();
Затем создаются три панели, которые будут добавляться в панель pCardPanel:
pBackgroundColor = new Panel();
pForegroundColor = new Panel();
pFont = new Panel();
Эти панели предназначены для размещения компонент, с помощью которых можно будет выбирать цвет фона и изображения, а также шрифт.
На следующем этапе создаются три списка, которые будут размещаться по одному на указанных панелях:
chBackgroundColor = new Choice();
chForegroundColor = new Choice();
chFont = new Choice();
Каждый такой список снабжается надписью, поясняющей его назначение. Надписи создаются следующим образом:
tBackgroundColor = new Label("Chose Background Color:");
tForegroundColor = new Label("Chose Foreground Color:");
tFont = new Label("Chose Font:");
Созданные метки и списки добавляются в панели, расположенные на страницах блокнота:
pBackgroundColor.add(tBackgroundColor);
pBackgroundColor.add(chBackgroundColor);
pForegroundColor.add(tForegroundColor);
pForegroundColor.add(chForegroundColor);
pFont.add(tFont);
pFont.add(chFont);
Метод keyDown
Когда пользователь нажимает клавишу, управление передается методу keyDown. Обработчик метода keyDown преобразует код нажатой клавиши nKey в текстовую строку типа String и затем отображает эту строку и содержимое двух полей объекта evt в окне аплета.
Преобразование выполняется в два приема.
Вначале код символа, имеющий тип int, преобразуется к типу char и записывается в ячейку массива типа char[], как это показано ниже:
char[] chKey;
String s;
chKey = new char[1];
chKey[0] = (char)nKey;
Затем этот массив, состоящий только из одного элемента, преобразуется в текстовую строку:
s = new String(chKey);
Далее метод ketDown получает контекст отображения, устанавливает в нем черный цвет и рисует в верхней части окна параметры клавиатурного события:
Graphics g = getGraphics();
g.setColor(Color.black);
g.drawString(s + " ", 10, 10);
g.drawString(" -> key: " + evt.key, 20, 10);
g.drawString(" mod: " + evt.modifiers, 100, 10);
Метод keyUp
Метод keyUp получает управление, когда пользователь отпускает ранее нажатую клавишу. Ему передаются те же параметры, что и только что рассмотренному методу keyDown.
Наш метод keyUp получает контекст отображения, а затем выполняет копирование верхней части окна, сдвигая ее вниз на размер символов текущего шрифта:
g.copyArea(0, 1,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - yHeight - 5,
0, yHeight + 1);
После сдвига метод закрашивает область ввода, расположенную в верхней части аплета, желтым цветом, расчищая таким образом место для ввода новой строки:
g.setColor(Color.yellow);
g.fillRect(1, 1,
dimAppWndDimension.width - 2, yHeight + 1);
Метод mouseDown
Когда пользователь делает щелчок левой клавишей мыши (напомним, что Java не работает с другими клавишами мыши), управление получает метод mouseDown.
Этот метод, переопределенный в нашем приложении, прежде всего сохраняет текущие координаты курсора мыши в переменной dimMouseCursor класса Dimension:
dimMouseCursor = new Dimension(x, y);
Событие, которое передается методу mouseDown через первый параметр, сохраняется в переменной ev:
ev = evt;
Далее метод mouseDown проверяет поле clickCount параметра evt:
if(evt.clickCount > 1)
showStatus("Mouse Double Click");
else
showStatus("Mouse Down");
В это поле записывается кратность щелчка мыши. Если пользователь сделал двойной щелчок, в строке состояния отображается текстовая строка Mouse Double Click, а если одинарный - строка Mouse Down.
Обратите внимание на метод showStatus. Этот метод позволяет аплету отобразить любую текстовую строку в строке состояния навигатора, поэтому он часто используется для отладки или выдачи текущей информации о состоянии аплета.
Заметим, однако, что в документе HTML может располагаться несколько разных аплетов, а строка состояния навигатора только одна. Поэтому сообщения от разных аплетов могут перекрывать друг друга, в результате чего в строке состояния появится только то сообщение, которое было записано туда последним.
После записи сообщения в строку состояния метод mouseDown перерисывывает окно аплета, вызывая для этого метод repaint:
repaint();
В результате вызова метода repaint происходит инициирование вызова метода paint, выполняющего перерисовку содержимого окна аплета. Однако не следует думать, будто метод repaint просто вызывает метод paint. Метод paint вызывается интерпретатором Java асинхронно по отношению к методу repaint в подходящий момент времени.
В последней строке метод mouseDown возвращает значение true:
return true;
Этим он сообщает, что обработка события завершена и это событие не нужно передавать обработчику из базового класса.
В начале своей работы метод mouseDown определяет, был ли сделан одинарный щелчок клавишей мыши, или двойной. Если был сделан двойной щелчок мышью, метод удаляет все элементы из массива list, а затем перерисовывает окно аплета, вызывая метод repaint:
lines.removeAllElements();
repaint();
После перерисовки окно аплета очищается от линий.
Если же был сделан одинарный щелчок клавишей мыши, метод mouseDown сохраняет текущие координаты курсора в переменных dmDown и dmPrev, а затем сбрасывает признак рисования:
dmDown = new Dimension(x, y);
dmPrev = new Dimension(x, y);
bDrawing = false;
Метод mouseDrag
До сих пор наши аплеты выполняли рисование только в методе paint, и так поступают большинство аплетов. Однако наш аплет должен рисовать линии во время перемещения курсора мыши, так как в противном случае пользователю не будет видно, как пройдет рисуемая линия.
Для того чтобы нарисовать что-либо в окне аплета, наобходимо получить контекст отображения. Методу paint этот контекст передается через парметр как объект класса Graphics. Если же вы собираетесь рисовать в другом методе, отличном от paint, необходимо получить контекст отображения, например, так:
Graphics g = getGraphics();
После получения контекста отображения и включения режима рисования (записью в переменную bDrawing значения true) метод mouseDrag стирает линию, которая была нарисована ранее, в процессе предыдущего вызова этого же метода:
g.setColor(Color.yellow);
g.drawLine(dmDown.width, dmDown.height,
dmPrev.width, dmPrev.height);
Для стирания линии мы рисуем ее на том же месте с использованием цвета, совпадающего с цветом фона.
Далее метод mouseDrag рисует новую линию черного цвета, соединяя точку, в которой была нажата клавиша мыши, с точкой текущего расположения курсора мыши:
g.setColor(Color.black);
g.drawLine(dmDown.width, dmDown.height, x, y);
После рисования линии координаты ее конца сохраняются в поле dmPrev для стирания этой линии при следующем вызове метода mouseDrag:
dmPrev = new Dimension(x, y);
return true;
Метод mouseMove
Метод mouseMove выглядит следующим образом:
public boolean mouseMove(Event evt, int x, int y)
{
return true;
}
Он ничего не делает, кроме того что возвращает значение true, блокируя обработку события, выполняемую в базовом классе.
Метод mouseMove не делает ничего, за исключением того, что он отключает режим рисования. Таким образом, простое перемещение курсора мыши над окном аплета не приводит к рисованию линий.
Метод mouseUp
Когда пользователь отпускает клавишу мыши, вызывается метод mouseUp. В его задачу входит сохранение текущих координат курсора мыши в поле dmUp, а также добавление нового элемента в массив lines, как это показано ниже:
dmUp = new Dimension(x, y);
lines.addElement(
new Rectangle(dmDown.width, dmDown.height, x, y));
repaint();
После добавления элемента в массив метод mouseUp инициирует перерисовку окна аплета, вызывая для этого метод repaint.
Заметим, что в качестве координат начала линии мы записываем в элемент массива координаты точки, где в последний раз пользователь нажимал курсор мыши. В качестве координат конца линии используются текущие координаты курсора на момент отпускания клавиши мыши.
Метод paint класса FirstPanel
Задачей метода paint класса FirstPanel является рисование рамки вокруг первой панели и текстовой строки в окне этой панели. В качестве параметра метод paint класса FirstPanel получает ссылку на контекст отображения для окна первой панели. Мы можем использовать этот контекст для того чтобы нарисовать что-нибудь внутри первой панели.
Процедура рисования не имеет никаких особенностей:
Dimension dimAppWndDimension = size();
g.drawRect(0, 0, dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
g.setFont(new Font("TimesRoman", Font.PLAIN, 12));
g.drawString("Первая панель", 10, 50);
Заметим, однако, что после завершения рисования мы вызываем метод paint из базового класса, позволяя этому классу выполнить свою обработку:
super.paint(g);
Метод paint класса MainFrameWnd
Метод paint получает в качестве параметра ссылку на контекст отображения, пригодный для рисования в нашем окне. Пользуясь этим контекстом, мы устанавливаем шрифт текста и рисуем текстовую строку. Затем мы вызываем метод paint из базового класса Frame, на основе которого создан наш класс MainFrameWnd:
g.setFont(new Font("Helvetica", Font.PLAIN, 12));
g.drawString("Окно класса Frame", 10, 50);
super.paint(g);
Метод paint класса SecondPanel
Метода paint класса SecondPanel выполняет те же самые операции, что и метод paint класса FirstPanel, однако ему передается контекст отображения второй панели. Поэтому он нарисует рамку и текстовую строку во второй панели, а не в первой или где-нибудь еще.
Метод paint
Наиболее интересен для нас метод paint, который выполняет рисование в окне аплета. Вот его исходный текст, созданный системой Java Applet Wizard:
public void paint(Graphics g)
{
g.drawString(
"Created with Microsoft Visual J++ Version 1.0",
10, 20);
}
Если посмотреть определение класса Applet, то в нем нет метода paint. В каком же классе определен этот метод?
Взглянем на определение класса Applet:
public class java.applet.Applet
extends java.awt.Panel
{
. . .
}
Во-первых, вы видите, что полное имя класса Applet есть java.applet.Applet. Включая оператором import библиотеку классов java.applet.*, мы включали и определение класса Applet.
Во-вторых, из определения класса можно заключить, что класс java.applet.Applet произошел от класса java.awt.Panel. Напомним, что определение классов java.awt.* также было включено в исходный текст нашего аплета оператором import.
Если класс java.applet.Applet был создан на базе класса java.awt.Panel, то нет ли в базовом классе определения метода paint?
Изучив исходный текст класса java.awt.Panel, убеждаемся, что такого метода там нет, однако сам класс java.awt.Panel произошел от класса java.awt.Container:
public class java.awt.Panel
extends java.awt.Container
{
. . .
}
Продолжим наши исследования. В классе java.awt.Container снова нет метода paint, но сам этот класс создан на базе класса java.awt.Component:
public abstract class java.awt.Container
extends java.awt.Component
{
. . .
}
Но и здесь метода paint нет. Этот метод определен в классе java.awt.Component, который, в свою очередь, произошел от класса java.lang.Object и реализует интерфейс java.awt.image.ImageObserver:
public abstract class java.awt.Component
extends java.lang.Object
implements java.awt.image.ImageObserver
{
. . .
}
Мы проследили иерархию классов от класса java.applet.Applet, на базе которого создан наш аплет, до класса java.lang.Object, который является базовым для всех классов в Java.
В обработчик метода paint мы добавили функции рисования.
В самом начале своей работы метод определяет текущие размеры окна аплета, вызывая для этого метод size:
Dimension dimAppWndDimension = size();
Метод size определен в классе Component, от которого в конечном счете наследуется класс Applet и класс нашего приложения Painter. Этот метод возвращает ссылку на объект класса Dimension, хранящего высоту и ширину объекта:
public class java.awt.Dimension
extends java.lang.Object
{
// -----------------------------------------------------
// Поля класса
// -----------------------------------------------------
public int height; // высота
public int width; // ширина
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
public Dimension();
public Dimension(Dimension d);
public Dimension(int width, int height);
// -----------------------------------------------------
// Метод
// -----------------------------------------------------
public String toString();
}
На следующем шаге после определения размеров окна наше приложение рисует в окне строку, а затем стирает содержимое всего окна:
g.drawString("Невидимая строка", 10, 20);
g.clearRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
В качестве начальных координат стираемой области мы указали точку (0, 0) - это верхний левый угол окна аплета. Ширина и высота стираемой области задана исходя из размеров аплета, полученных от метода size.
Для того чтобы изменить цвет фона окна, ме его закрашиваем (хотя могли бы воспользоваться и методом setBackground). Это можно сделать методом fillRect. Вначале при помощи метода setColor мы выбираем в контекст отображения желтый цвет, а затем закрашиваем всю внутреннюю область окна аплета методом fillRect:
g.setColor(Color.yellow);
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
В методе paint прежде всего мы определяем полную высоту символов шрифта, которая будет использована при выводе строк. Высота шрифта определяется следующим образом:
FontMetrics fm = g.getFontMetrics();
yStep = fm.getHeight();
Зная высоту шрифта и количество элементов в списке доступных шрифтов, мы можем изменить размер окна аплета по вертикали таким образом, чтобы в нем поместились все строки. Количество элементов в массиве fntlist равно fntlist.length, а полную высоту шрифта мы только что определили. Для изменения высоты окна аплета мы используем метод resize:
resize(150, 20 + yStep * fntlist.length);
Далее мы определяем новые размеры окна аплета, закрашиваем фон окна желтым цветом и обводим окно тонкой рамкой черного цвета:
Dimension dimAppWndDimension = size();
g.setColor(Color.yellow);
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
g.setColor(Color.black);
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
Эту операцию мы уже выполняли в предыдущем приложении.
Список установленных шрифтов выводится достаточно просто в цикле:
for(int i = 0; i < fntlist.length; i++)
{
g.drawString(fntlist[i], 10, yStart + yStep * i);
}
Здесь содержимое параметра цикла (переменной i) меняется от 0 до количества элементов в массиве length. Каждая новая строка рисуется со смещением, равным полной высоте символов текущего шрифта, выбранного в контекст отображения.
После закрашивания фона желтым цветом и рисования вокруг окна аплета черной рамки метод paint анализирует значение параметра m_Type1 и выбирает в контекст отображения шрифт для рисования первой строки:
if(m_Type1.equals("Bold"))
g.setFont(new Font(m_Font1, Font.BOLD, 25));
else if(m_Type1.equals("Italic"))
g.setFont(new Font(m_Font1, Font.ITALIC, 25));
else if(m_Type1.equals("Plain"))
g.setFont(new Font(m_Font1, Font.PLAIN, 25));
Для сравнения строк класса String мы используем метод equals, который возвращает значение true при совпадении с заданной строкой и false в противном случае.
Методу выбора шрифта setFont мы передаем объект класса Font, созданный конструктором.
Конструктор получает в качетсве первого параметра содержимое поля класса m_Font1, которое соответствует значению параметра аплета с именем Font1.
Значение второго параметра (стиль шрифта) выбирается исходя из значения параметра аплета с именем m_Type1. Здесь мы указываем константы, определенные в классе Font.
И, наконец, третий параметр конструктора класса Font задает размер символов шрифта, равный 25 пикселам.
После выбора шрифта мы выполняем отступ от верхней границы окна и рисуем первую строку в позиции (0, yCurrent):
yCurrent += yStart;
g.drawString(m_Str1, 10, yCurrent);
На следующем этапе метод paint получает метрику только что выбранного шритфа и увеличивает текущую позицию yCurrent на величину полной высоты символов шрифта, полученную с помощью метода getHeight:
FontMetrics fm = g.getFontMetrics();
yCurrent += fm.getHeight();
Далее эта же процедура повторяется для остальный четырех отображаемых в окне аплета строк.
Экспериментируя с аплетом, попробуйте изменить параметры, передаваемые аплету в документе HTML. Укажите, например, несуществующий шрифт и посмотрите, какой шрифт будет выбран навигатором для отображения.
В начале своей работы метод paint определяет текущие размеры окна аплета, закрашивает это окно в желтый цвет и рисует вокруг него тонкую рамку черного цвета. Все это делается исключительно для того чтобы выделить аплет в документе HTML и обозначить его границы.
Далее метод paint отображает текущие координаты курсора мыши, взяв их из переменной ev:
g.drawString("(" + ev.x + "," + ev.y + ")", ev.x, ev.y);
После обычной для наших аплетов раскраски фона и рисования рамки метод paint перебирает в цикле все элементы массива lines, рисуя линии:
for (int i=0; i < lines.size(); i++)
{
Rectangle p = (Rectangle)lines.elementAt(i);
g.drawLine(p.width, p.height, p.x, p.y);
}
Для объектов класса Vector можно использовать метода size, возвращающий количество элементов в массиве, чем мы воспользовались для проверки условия выхода из цикла.
Чтобы извлечь элемент массива по его номеру, мы воспользовались методом elementAt, передав ему через единственный параметр номер извлекаемого элемента.
Так как в массиве хранятся объекты класса Rectangle, перед инициализацией ссылки p мы выполняем преобразование типов.
Перед завершением работы метод paint сбрасывает признак рисования, записывая в поле bDrawing значение false:
bDrawing = false;
Метод paint закрашивает окно аплета желтым цветом и обводит его рамкой. Никакой другой работы этот метод не выполняет.
Метод paint не содержит никакого кода для рисования кнопок, так как эта задача решается в рамках класса Button. После раскрашивания фона окна аплета и рисования рамки вокруг него, метод paint пишет название нажатой кнопки в окне аплета с помощью метода drawString:
g.drawString("Button (\"" + sTextLabel + "\") pressed",
10, 70);
В методе paint мы не закрашиваем желтым цветом окно аплета, так как на этапе инициализации в обработчике метода init был установлен желтый цвет фона окна. Однако черную рамку вокруг границы окна аплета мы все же рисуем.
Основная задача метода paint заключается в отображении в нижней части окна аплета списка включенных переключателей. Для формирования этой строки мы создаем объект s класса String:
String s = new String("> ");
Далее мы проверяем по очереди состояние всех переключателей с независимой фиксацией, дописывая к строке s название включенных переключателей:
if(chbox1.getState())
s = s + chbox1.getLabel() + ", ";
if(chbox2.getState())
s = s + chbox2.getLabel() + ", ";
if(chbox3.getState())
s = s + chbox3.getLabel() + ", ";
Для определения текущего состояния переключателей мы вызываем метод getState. Этот метод возвращает значение true для включенного переключателя и false - для выключенного. Название переключателя легко определить с помощью метода getLabel.
Проверка состояний переключателей с зависимой фиксацией выполняется аналогично, но с учетом того, что одновременно может быть включен только один такой пеерключатель:
if(rdbox1.getState())
s = s + rdbox1.getLabel();
else if(rdbox2.getState())
s = s + rdbox2.getLabel();
else if(rdbox3.getState())
s = s + rdbox3.getLabel();
else if(rdbox4.getState())
s = s + rdbox4.getLabel();
После завершения формирования строки s она отображается в окне аплета методом drawString:
g.drawString(s, 10, 150);
Обработчик метода paint рисует рамку вокруг окна аплета и текстовую строку в средней части этого окна.
Dimension dimAppWndDimension = size();
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
g.drawString("Смотри на цвет фона и текста!", 10, 50);
При этом мы не указали цвет фона, а также цвет изображения. При этом используются цвета, установленные методом action при выборе соответствующих строк из списков.
Обработчик метода paint рисует рамку вокруг окна аплета и текстовую строку в средней части этого окна.
В нижней части окна аплета метод paint отображает выделенные в списках цвета фона и изображения:
g.drawString("Background: " + sSelBackground, 10, 160);
g.drawString("Foreground: " + sSelForeground, 10, 190);
Единственное, что делает метод paint, - это рисование рамки черного цвета вокруг окна аплета.
После рисования рамки черного цвета вокруг окна аплета метод paint отобаржает текущее содержимое строки str в нижней части окна:
g.drawString("> " + str, 10, 100);
После рисования рамки черного цвета вокруг окна аплета метод paint отобаржает текущее содержимое строки str в нижней части окна:
g.drawString("> " + str, 10, 100);
Метод paint рисует в окне панели pDraw, а не в главном окне аплета. Для этого метод paint получает контекст отображения этой панели, вызывая для этого метод getGraphics:
Graphics gpDraw;
gpDraw = pDraw.getGraphics();
Далее метод paint определяет размеры панели pDraw и рисует рамку вокруг окна этой панели:
Dimension dimAppWndDimension = pDraw.size();
gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
После того как рамка будет нарисована, метод paint устанавливает в панели pDraw шрифт, название которого хранится в староке sFontName. Для этого используется контекст отображения gpDraw, полученный нами ранее для панели pDraw:
gpDraw.setFont(new Font(sFontName, Font.PLAIN, 12));
Текстовая строка отображается с использованием текущего цвета изображения и текущего шрифта, установленного в контексте отображения gpDraw, при помощи метода drawString:
gpDraw.drawString("Смотри на шрифт, цвет фона и текста!",
10, 50);
Затем метод paint обводит панель блокнота рамкой. Вначале он получает контекст отображения для панели блокнота, как это показано ниже:
gpDraw = pCardPanel.getGraphics();
После получения контекста отображения метод paint определяет размеры панели блокнота и рисует рамку вокруг его окна:
dimAppWndDimension = pCardPanel.size();
gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
Метод start
Метод start вызывается после метода init в момент, когда пользователь начинает просматривать документ HTML с встроенным в него аплетом. Система Java Applet Wizard создает заглушку, переопределяющую метод start из базового класса:
public void start()
{
// Сделать: Добавьте сюда дополнительный код,
// который должен работать при запуске аплета
}
Вы можете модифицировать текст этого метода, если при каждом посещении пользователем страницы с аплетом необходимо выполнять какую-либо инициализацию.
Метод stop
Дополнением к методу start служит метод stop. Он вызывается, когда пользователь покидает страницу с аплетом и загружает в окно навигатора другую страницу. Заметим, что метод stop вызывается перед методом destroy.
По умолчанию система Java Applet Wizard переопределяет метод stop базового класса Applet следующим образом:
public void stop()
{
// Сделать: Добавьте сюда дополнительный код,
// который должен работать при остановке аплета
}
Методы класса Graphics
В качестве базового для класса Graphics (полное название класса java.awt.Graphics) выступает классс java.lang.Object. В виду важности класса Graphics мы приведем его определение с комментариями:
public abstract class java.awt.Graphics
extends java.lang.Object
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
protected Graphics();
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Стирание содержимого прямоугольной области
public abstract void
clearRect(int x, int y, int width, int height);
// Задание области ограничения вывода
public abstract void
clipRect(int x, int y, int width, int height);
// Копирование содержимого прямоугольной области
public abstract void
copyArea(int x, int y, int width,
int height, int dx, int dy);
// Создание контекста отображения
public abstract Graphics create();
// Создание контекста отображения
public Graphics create(int x, int y,
int width, int height);
// Удаление контекста отображения
public abstract void dispose();
// Рисование прямоугольной области с трехмерным
// выделением
public void draw3DRect(int x, int y, int width,
int height, boolean raised);
// Рисование сегмента
public abstract void drawArc(int x, int y,
int width, int height, int startAngle, int arcAngle);
// Рисование текста из массива байт
public void drawBytes(byte data[], int offset,
int length, int x, int y);
// Рисование текста из массива символов
public void drawChars(char data[], int offset,
int length, int x, int y);
// Рисование растрового изображения
public abstract boolean
drawImage(Image img, int x, int y, Color bgcolor,
ImageObserver observer);
// Рисование растрового изображения
public abstract boolean
drawImage( Image img, int x, int y,
ImageObserver observer);
// Рисование растрового изображения
public abstract boolean
drawImage(Image img, int x, int y,
int width, int height, Color bgcolor,
ImageObserver observer);
// Рисование растрового изображения
public abstract boolean
drawImage(Image img, int x, int y,
int width, int height, ImageObserver observer);
// Рисование линии
public abstract void drawLine(int x1, int y1,
int x2, int y2);
// Рисование овала
public abstract void drawOval(int x, int y,
int width, int height);
// Рисование многоугольника
public abstract void
drawPolygon(int xPoints[], int yPoints[], int nPoints);
// Рисование многоугольника
public void drawPolygon(Polygon p);
// Рисование прямоугольника
public void drawRect(int x, int y,
int width, int height);
// Рисование прямоугольника с круглыми углами
public abstract void
drawRoundRect(int x, int y, int width,
int height, int arcWidth, int arcHeight);
// Рисование текстовой строки
public abstract void
drawString(String str, int x, int y);
// Рисование заполненного прямоугольника с
// трехмерным выделением
public void
fill3DRect(int x, int y, int width,
int height, boolean raised);
// Рисование заполненного сегмента круга
public abstract void
fillArc(int x, int y, int width,
int height, int startAngle, int arcAngle);
// Рисование заполненного овала
public abstract void
fillOval(int x, int y, int width, int height);
// Рисование заполненного многоугольника
public abstract void
fillPolygon(int xPoints[], int yPoints[], int nPoints);
// Рисование заполненного многоугольника
public void fillPolygon(Polygon p);
// Рисование заполненного прямоугольника
public abstract void
fillRect( int x, int y, int width, int height);
// Рисование заполненного прямоугольника
// с круглыми углами
public abstract void
fillRoundRect(int x, int y, int width, int height,
int arcWidth, int arcHeight);
// Прослеживание вызова метода dispose
public void finalize();
// Определение границ области ограничения вывода
public abstract Rectangle getClipRect();
// Определение цвета, выбранного в контекст отображения
public abstract Color getColor();
// Определение шрифта, выбранного в контекст отображения
public abstract Font getFont();
// Определение метрик текущего шрифта
public FontMetrics getFontMetrics();
// Определение метрик заданного шрифта
public abstract FontMetrics getFontMetrics(Font f);
// Установка цвета для рисования в контексте отображения
public abstract void setColor(Color c);
// Установка текущего шрифта в контексте отображения
public abstract void setFont(Font font);
// Установка режима рисования
public abstract void setPaintMode();
// Установка маски для рисования
public abstract void setXORMode(Color c1);
// Получение текстовой строки, представляющей
// данный контекст отображения
public String toString();
// Сдвиг начала системы координат
// в контексте отображения
public abstract void translate(int x, int y);
}
Рассмотрим назначение основных методов, сгруппировав их по выполняемым функциям.
Методы mouseUp, mouseDrag, mouseEnter, mouseExit
Обработчики методов mouseUp, mouseDrag, mouseEnter и mouseExit выглядят одинаково:
public boolean mouseUp(Event evt, int x, int y)
{
// Выводим сообщение в строке состояния
showStatus("Mouse Up");
return true;
}
Пользуясь методом showStatus, эти методы записывают соответствующее сообщение в строку состояния и возвращают значение true.
Многострочное текстовое поле класса TextArea
Если вам нужно поле для ввода многострочной информации, обратите внимание на класс TextArea. С его помощью вы можете создать многострочное поле заданной ширины и высоты, снабженное полосами просмотра.
Класс TextArea создан на базе класса TextComponent, рассмотренном нами ранее, поэтому для работы с многострочными полями вы можете использовать методы этого класса. В частности, вам доступен метод, с помощью которого можно получать из онка редактирования не весь текст, а только выделенную пользователем область.
Краткое описание класса TextArea мы привели ниже:
public class java.awt.TextArea
extends java.awt.TextComponent
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание поля без текста и без указания размеров
public TextArea();
// Создание поля без текста с указанием размеров
public TextArea(int rows, int cols);
// Создание поля с текстом без указания размеров
public TextArea(String text);
// Создание поля с текстом и с указанием размеров
public TextArea(String text, int rows, int cols);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createTextArea
public void addNotify();
// Добавление текста в поле редактирования
public void appendText(String str);
// Определение количества столбцов поля
public int getColumns();
// Определение количества строк поля
public int getRows();
// Добавление текста в поле редактирования
// начиная с заданной позиции
public void insertText(String str, int pos);
// Определение минимальных размеров области
// для размещения многострочного текстового поля
public Dimension minimumSize();
// Определение минимальных размеров области
// для размещения многострочного текстового поля
// с заданным количеством строк и столбцов
public Dimension minimumSize(int rows, int cols);
// Получение строки параметров
protected String paramString();
// Определение предпочтительных размеров области
// для размещения многострочного текстового поля
public Dimension preferredSize();
// Определение предпочтительных размеров области
// для размещения многострочного текстового поля
// с заданным количеством строк и столбцов
public Dimension preferredSize(int rows, int cols);
// Замещение блока текста, начиная с первой позиции
// и до второй позиции
public void replaceText( String str, int start, int end);
}
Когда вы создаете многострочное текстовое поле редактирования, то можете использовать конструктор, допускающий указание размеров поля в строках и столбцах:
TextArea txt;
txt = new TextArea("Введите строку текста", 5, 35);
Созданное поле добавляется в окно аплета методом add.
Отметим, что в классе TextArea есть методы для работы с блоками текста (вставка и замена), а также методы, с помощью которых можно определить количество строк и столбцов в поле редактирования.
Многоугольники
Для рисования многоугольников в классе Graphics предусмотрено четыре метода, два из которых рисуют незаполненные многоугольники, а два - заполненные.
Первый метод рисует незаполненный многоугольник, заданный массивами координат по осям X и Y:
public abstract void
drawPolygon(int xPoints[], int yPoints[], int nPoints);
Через параметры xPoints и yPoints передаются, соответственно, ссылки на массивы координат по оис X и Y. Параметр nPoints задает количество точек в массивах.
На рис. 3.6 показан многоугольник, нарисованный методом drawPolygon.
Рис. 3.6. Многоугольник, нарисованный методом drawPolygon
В этом многоугольнике шесть вершин с координатами от (x0,y0) до (x5, y5), причем для того чтобы он стал замкнутым,координаты первой и последней вершины совпадают.
Второй метод также рисует незаполненный многоугольник, однако в качетсве параметра методу передается ссылка на объект Polygon:
public void drawPolygon(Polygon p);
Класс Polygon достаточно прост, поэтому мы приведем его описание полностью:
public class java.awt.Polygon
extends java.lang.Object
{
// -----------------------------------------------------
// Поля класса
// -----------------------------------------------------
public int npoints; // количество вершин
public int xpoints[]; // массив координат по оси X
public int ypoints[]; // массив координат по оси Y
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
public Polygon();
public Polygon(int xpoints[], int ypoints[], int npoints);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Добавление вершины
public void addPoint(int x, int y);
// Получение координат охватывающего прямоугольника
public Rectangle getBoundingBox();
// Проверка, находится ли точка внутри многоугольника
public boolean inside(int x, int y);
}
Ниже мы показали фрагмент кода, в котором создается многоугольник, а затем в него добавляется несколько точек. Многоугольник рисуется методом drawPolygon:
Polygon p = new Polygon();
p.addPoint(270, 239);
p.addPoint(350, 230);
p.addPoint(360, 180);
p.addPoint(390, 160);
p.addPoint(340, 130);
p.addPoint(270, 239);
g.drawPolygon(p);
Если вам нужно нарисовать заполненный многоугольник (рис. 3.7), то для этого вы можете воспользоваться методами, приведенными ниже:
public abstract void
fillPolygon(int xPoints[], int yPoints[], int nPoints);
public void fillPolygon(Polygon p);
Первый из этих методов рисует многоугольник, координаты вершин которого заданы в массивах, второй - получая объект класса Polygon в качестве параметра.
Рис. 3.6. Многоугольник, нарисованный методом drawPolygon
Мобильность Java
В свое время вы слышали, что язык программирования С является мобильным в том смысле, что имеется принципиальная возможность переноса программ, составленных на этом языке, на различные платформы.
Однако следует отметить, что создание действительно многоплатформных приложений - непростая задача. К сожалению, дело не ограничивается необходимостью перекомпиляции исходного текста программы для работы в другой среде. Много проблем возникает с несовместимостью программных интерфейсов различных операционных систем и графических оболочек, реализующих пользовательский интерфейс.
Вспомните хотя бы проблемы, связанные с переносом 16-разрядных приложений Windows в 32-разрядную среду Microsoft Windows 95 и Microsoft Windows NT. Даже если вы тщательно следовали всем рекомендациям Microsoft, разрабатывая приложения так, чтобы они могли работать в будующих версиях Windows, едва ли вам удасться просто перекомпилировать исходные тексты, не изменив в них ни строчки. Ситуация еще больше ухудшается, если вам нужно, например, перенести исходные тексты приложения Windows в среду операционной системы IBM OS/2 или в оболочку X-Windows операционной системы UNIX. А ведь есть еще компьютеры Apple, рабочие станции с процессором RISC и другие!
Как нетрудно заметить, даже если стандартизовать язык программирования для всех платформ, проблемы совместимости с программным интерфейсом операционной системы значительно усложняют перенос программ на различные платформы. И, конечно, вы не можете мечтать о том, чтобы загрузочный модуль одной и той же программы мог работать без изменений в среде различных операционных систем и на различных платформах. Если программа подготовлена для процессора Intel, она ни за что не согласится работать на процессоре Alpha или каком-либо другом.
В результате создавая приложение, способное работать на различных платформах, вы вынуждены фактически создавать несколько различных приложений и сопровождать их по отдельности.
На рис. 1.1 мы показали, как приложение, изначально разработанное для Microsoft Windows NT, переносится на платформу Apple Macintosh.
Рис. 1.1. Перенос приложения с платформы Windows NT на платформу Apple Macintosh
Вначале программист готовит исходные тексты приложения для платформы Microsoft Windows NT и отлаживает их там. Для получения загрузочного модуля исходные тексты компилируются и редактируются. Полученный в результате загрузочный модуль может работать на процессоре фирмы Intel в среде операционной системы Microsoft Windows NT.
Для того чтобы перенести приложение в среду операционной системы компьютера Apple Macintosh, программист вносит необходимые изменения в исходные тексты приложения. Эти изменения необходимы из-за различий в программном интерфейсе операционной системы Microsoft Windows NT и операционной системы, установленной в Apple Macintosh. Далее эти исходные тексты транслируются и редактируются, в результате чего получается загрузочный модуль, способный работать в среде Apple Macintosh, но не способный работать в среде Microsoft Windows NT.
Программа на языке Java компилируется в двоичный модуль, состоящий из команд виртуального процессора Java. На настоящий момент созданы только первые модели физического процессора, способного выполнять эти двоичные команды, однако интерпретаторы Java имеются на всех основных компьютерных платформах. Разумеется, на каждой платформе используется свой интерпретатор, или, точнее говоря, свой виртуальный процессор Java.
Если ваше приложение Java (или аплет) должно работать на нескольких платформах, нет необходимости компилировать его исходные тексты несколько раз. Вы можете откомпилировать и отладить приложение Java на одной, наиболее удобной для вас платформе. В результате вы получите двоичный модуль, пригодный для любой платформы, где есть виртуальный процессор Java.
Сказанное иллюстрируется на рис. 1.2.
Рис. 1.2. Подготовка приложения Java для работы на разных платформах
Таким образом, приложение Java компилируется и отлаживается только один раз, что уже значительно лучше. Остается, правда, вопрос - как быть с программным интерфейсом операционной системы, который отличается для разных платформ?
Здесь разработчиками Java предлагается достаточно неплохое решение. Приложение Java не обращается напрямую к интерфейсу операционной системы. Вместо этого оно пользуется готовыми стандартными библиотеками классов, содержащими все необходимое для организации пользовательского интерфейса, обращения к файлам, для работы в сети и так далее.
Внутренняя реализация библиотек классов, разумеется, зависит от платформы. Однако все загрузочные модули, реализующие возможности этих библиотек, поставляются в готовом виде вместе с виртуальной машиной Java, поэтому программисту не нужно об этом заботиться. Для операционной системы Microsoft Windows, например, поставляются библиотеки динамической загрузки DLL, внутри которых запрятана вся функциональность стандартных классов Java.
Абстрагируясь от аппаратуры на уровне библиотек классов, программисты могут больше не заботиться о различиях в реализации программного интерфейса конкретных операционных систем. Это позволяет создавать по-настоящему мобильные приложения, не требующие при переносе на различные платформы перетрансляции и изменения исходного текста.
Еще одна проблема, возникающая при переносе программ, составленных на языке программирования С, заключается в том, что размер области памяти, занимаемой переменными стандартных типов, различный на разных платформах. Например, в среде операционной системы Microsoft Windows версии 3.1 переменная типа int в программе, составленной на С, занимает 16 бит. В среде Microsoft Windows NT этот размер составляет 32 бита.
Очевидно, что трудно составлять программу, не зная точно, сколько имеется бит в слове или в байте. При переносе программ на платформы с иной разрядностью могут возникать ошибки, которые трудно обнаружить.
В языке Java все базовые типы данных имеют фиксированную разрядность, которая не зависит от платформы. Поэтому программисты всегда знают размеры переменных в своей программе.
Наследование
С помощью ключевого слова extends вы можете унаследовать один класс (дочерний) от другого (базового).
Множественное наследование не допускается. Таким образом, для каждого дочернего класса может быть только один базовый класс. При необходимости, однако, этот дочерний класс может реализовывать произвольное количество интерфейсов.
Для ссылки на методы базового класса вы должны использовать ключевое слово super.
При необходимости вы можете вызвать в первой исполняемой строке конструктора дочернего класса конструктор базового класса (опять же с помощью ключевого слова super).
Нажатие клавиши мыши
Переопределив метод mouseDown, вы сможете отслеживать нажатия клавиши мыши. Прототип этого метода приведен ниже:
public boolean mouseDown(Event evt, int x, int y);
Через параметр evt методу передается ссылка на объект Event, с помощью которой метод может получить полную информацию о событии.
Анализируя содержимое параметров x и y, приложение может определить координаты курсора на момент возникновения события.
Заметим, что для отслеживания двойного щелчка мыши не предусмотрено никакого отдельного метода. Однако анализируя содержимое поля clickCount переменной evt, вы можете определить кратность щелчка мыши:
if(evt.clickCount > 1)
// Двойной щелчок
showStatus("Mouse Double Click");
else
// Одинарный щелчок
showStatus("Mouse Down");
Обработка событий от кнопки
Для обработки событий, создаваемых кнопками и другими компонентами, вы можете переопределить метод handleEvent, описанный нами ранее. Однако существует более простой способ.
Этот способ основан на переопределении метода action, который получает управление, когда пользователь совершает какое-либо действие с компонентом. Под действием подразумевается нажатие на кнопку, завершение ввода текстовой строки, выбор элемента из списка, изменение состояния переключателя и так далее.
Прототип метода action представлен ниже:
public boolean action(Event evt, Object obj)
{
. . .
}
В качестве первого параметра методу передается ссылка на объект класса Event, содержащий всю информацию о событии. Второй параметр представляет собой ссылку на объект, вызвавший появление события.
Как обрабатывать событие в методе action?
Прежде всего необходимо проверить, объект какого типа создал событие. Это можно сделать, например, следующим образом:
if(evt.target instanceof Button)
{
. . .
return true;
}
return false;
Здесь мы с помощью оператора instanceof проверяем, является ли объект, вызвавший появление события, объектом класса Button.
Далее, если в окне аплета имеется несколько кнопок, необходимо выполнить ветвление по ссылкам на объекты кнопок, как это показано ниже:
if(evt.target.equals(btn1))
{
. . .
}
else if(evt.target.equals(btn2))
{
. . .
}
. . .
else
{
return false;
}
return true;
Тем из вас, кто создавал приложения Windows на языке программирования С, этот фрагмент кода может напомнить длинный переключатель switch обработки сообщений Windows.
Обработка событий от списка класса List
В отличие от списка класса Choice, для выбора строки (или нескольких строк) из списка класса List, пользователь должен сделать двойной щелчок левой клавишей мыши по выделенному элементу (или элементам, если выделено несколько элементов). При этом событие можно обработать переопределенным методом action, как мы это делали для списка класса Choice.
Однако список класса List создает события не только при двойном щелчке, но и при выделении или отмены выделения элементов, сделанном пользователем одинарным щелчком клавиши мыши. Аплет может перехватывать и обрабатывать такие события, переопределив метод handleEvent. Пример такой обработки вы найдете в исходных текстах приложения ListBox.
Окна класса Frame
Ниже мы привели краткое описание класса Frame. Так как этот класс реализует интерфейс java.awt.MenuContainer, окно класса Frame может содержать меню.
public class java.awt.Frame
extends java.awt.Window
implements java.awt.MenuContainer
{
// -----------------------------------------------------
// Поля
// -----------------------------------------------------
// Различные типы курсоров
public final static int CROSSHAIR_CURSOR;
public final static int DEFAULT_CURSOR;
public final static int E_RESIZE_CURSOR;
public final static int HAND_CURSOR;
public final static int MOVE_CURSOR;
public final static int N_RESIZE_CURSOR;
public final static int NE_RESIZE_CURSOR;
public final static int NW_RESIZE_CURSOR;
public final static int S_RESIZE_CURSOR;
public final static int SE_RESIZE_CURSOR;
public final static int SW_RESIZE_CURSOR;
public final static int TEXT_CURSOR;
public final static int W_RESIZE_CURSOR;
public final static int WAIT_CURSOR;
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание окна без заголовка
public Frame();
// Создание окна с заголовоком
public Frame(String title);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createFrame
public void addNotify();
// Удаление окна и освобождение связанных с ним ресурсов
public void dispose();
// Определение типа курсора
public int getCursorType();
// Получение пиктограммы, установленной для окна
public Image getIconImage();
// Получение ссылки на главное меню
public MenuBar getMenuBar();
// Получение заголовка окна
public String getTitle();
// Определение возможности изменения
// размеров окна пользователем
public boolean isResizable();
// Получение строки параметров
protected String paramString();
// Удаление компоненты меню
public void remove(MenuComponent m);
// Установка типа курсора
public void setCursor(int cursorType);
// Установка пиктограммы
public void setIconImage(Image image);
// Установка главного меню
public void setMenuBar(MenuBar mb);
// Включение или выключение возомжности
// изменения размеров окна
public void setResizable(boolean resizable);
// Установка заголовка окна
public void setTitle(String title);
}
Для того чтобы создать свое окно на базе класса Frame, вы должны определить свой класс, унаследовав его от класса Frame следующим образом:
class MainFrameWnd extends Frame
{
. . .
public MainFrameWnd(String sTitle)
{
super(sTitle);
. . .
resize(400, 200);
}
. . .
}
Если мы будем создавать окно с заголовком, нам необходимо соответствующим образом определить конструктор класса этого окна. В частности, наш конструктор должен вызывать конструктор базового класса, передавая ему в качестве параметра строку заголовка окна. Напомним, что конструктор базового класса должен вызываться в конструкторе дочернего класса перед выполнением каких-либо других действий.
Обратите также внимание на вызов метода resize. Этот вызов необходим для задания размеров окна.
В конструкторе вы можете определить различные параметры создаваемого вами окна, например, указать форму курсора, пиктограмму, представляющую окно, задать меню, определить возможность изменения размеров окна и так далее. Мы остановимся подробнее на процедуре добавления меню к окну класса Frame, так как она требует пояснений. С изменением других характеристик окна вы справитесь самостоятельно.
При создании окна классов Frame и Dialog для них устанавливается режим размещения BorderLayout. Если вам нужен другой режим размещения, необходимо установить его явным образом.
Кроме того, созданное окно появится на экране только после вызова для него метода show.
Убрать окно с экрана вы можете методом hide. Этот метод прячет окно, но оставляет в памяти все связанные с ним ресурсы, поэтому вы сможете вновь отобразить спрятанное окно, вызвав метод show.
В отличие от метода hide, метод dispose удаляет окно и освобождает все связанные с ним ресурсы. Этот метод применяется для окончательного удаления окна с экрана и из памяти.
Еще одно замечание касается обработки операции уничтожения окна при помощи двойного щелчка левой клавиши мыши по системному меню окна или при помощи кнопки уничтожения окна, расположенной в правой части заголовка.
Когда пользователь пытается уничтожить окно класса Frame или Dialog подобным образом, возникает событие Event.WINDOW_DESTROY. Вы должны предусмотреть обработку этого события, выполняя действия, соответствующие логике работы вашего окна. Обычно окно уничтожается вызовом метода dispose, как это показано ниже:
public boolean handleEvent(Event evt)
{
if(evt.id == Event.WINDOW_DESTROY)
{
dispose();
return true;
}
else
return super.handleEvent(evt);
}
Описание исходного текста
Наиболее важными для нас являются методы init и paint.
В исходном тексте класса MouseClick мы определили поля класса с именами dimMouseCursor и ev:
Dimension dimMouseCursor;
Event ev;
Первое из них предназначено для хранения координат курсора в момент возникновения события, а второе - хранит ссылку на это событие.
В нашем аплете мы будем создавать объект класса Vector, который является массивом с динамически изменяемым размером. Этот класс имеет полное имя java.util.Vector, поэтому мы подключаем соответствующую библиотеку классов:
import java.util.*;
Наш аплет определяет несколько полей в своем классе и переопределяет несколько методов базового класса.
После того как исходный текст приложения был создан системой Java Applet Wizard, мы добавили поля для хранения ссылок на кнопки и текстовую строку для записи метки нажатой кнопки, а также добавили и изменили несколько методов.
Рассмотрим поля класса CheckBoxes и переопределенные нами методы.
Рассмотрим поля класса ChoiceList и переопределенные нами методы.
В классе ListBox мы добавили четыре поля и переопределили несколько методов.
В классе TextLabel мы определили четыре поля и несколько методов.
В классе TxtField мы определили три поля и несколько методов.
В классе TextEdit мы определили четыре поля и несколько методов.
В исходном тексте приложения Grid вы сможете разобраться саомстоятельно. Заметим только, что в методе init мы вызываем метод setLayout, передавая ему объект класса GridLayout:
setLayout(new GridLayout(3, 3));
В свою очередь, параметры конструктора объекта GridLayout определяют, что для размещения компонент будет использована таблица, состоящая из трех строк и трех столбцов.
Компоненты (кнопки) добавляются в окно аплета хорошо известным вам способом - с помощью метода add:
add(btn1);
add(btn2);
add(btn3);
add(btn4);
add(btn5);
add(btn6);
add(btn7);
add(btn8);
Как видно из рис. 6.2, таблица заполняется кнопками слева направо и сверху вниз, как это и предполагает режим компоновки GridLayout.
Все самое интересное в приложении Border происходит в методе init, поэтому мы ограничимся описанием только этого метода.
Прежде всего метод init устанавливает желтый цвет фона:
setBackground(Color.yellow);
И хотя весь фон в нашем аплете закрыт кнопками, вы можете изменить это, выбрав конструктор класса BorderLayout, допускающий задание зазора между компонентами по вертикали и горизонтали.
Далее метод init создает пять кнопок для размещения в окне аплета. Здесь тоже для вас нет ничего нового:
btn1 = new Button("Button North");
btn2 = new Button("Button East");
btn3 = new Button("Button West");
btn4 = new Button("Button South");
btn5 = new Button("Button Center");
Далее мы устанавливаем режим размещения компонент в окне контейнера, вызывая для этого метод setLayout:
setLayout(new BorderLayout());
В качестве параметра методу setLayout передается ссылка на только что созданный объект класса BorderLayout. Так как выбран конструктор класса BorderLayout без параметров, зазор между компонентами будет отсутствовать.
Добавление компонент выполняется методом add с указанием направления расположения компоненты:
add("North", btn1);
add("East", btn2);
add("West", btn3);
add("South", btn4);
add("Center", btn5);
Заметим, что нельзя размещать несколько компонент в одном и том же направлении.
Приведем описание полей и методов, определенных в нашем аплете.
В классе Notebook определено довольно много полей и переопределено несколько методов.
Как мы уже говорили, в приложении Panel2 мы создали два класса, взяв для них в качестве базового класс Panel. Имена этих классов - FirstPanel и SecondPanel. После трансляции проекта системой Microsoft Visual J++ получаются три двоичных файла с именами Panel2.class, FirstPanel.class и SecondPanel.class - по одному для каждого класса.
Рассмотрим по очереди поля и методы классов, определенных в нашем приложении.
Описание исходных текстов
Если при создании шаблона аплета с помощью системы Java Applet Wizard вы указываете, что аплету передаются параметры и определяете их список, система Java Applet Wizard организует для вас прием и хранение параметров. Поэтому обработка параметров аплета не отнимет у вас много сил.
Какие строки добавляются системой Java Applet Wizard для обработки параметров?
Описание класса List
В классе List определено два конструктора и довольно много различных методов. Ниже мы привели краткое описание класса List:
public class java.awt.List
extends java.awt.Component
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Конструктор без параметров
public List();
// Конструктор, позволяющий указать количество
// отображаемых строк и флаг одновременного
// выбора нескольких элементов
public List(int rows, boolean multipleSelections);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Добавление элемента в список
public void addItem(String item);
// Добавление элемента в список с указанием номера позиции
public void addItem(String item, int index);
// Вызов метода createList
public void addNotify();
// Переключение списка в режим, при котором возможно
// выбирать одновременно несколько элементов
public boolean allowsMultipleSelections();
// Удаление из списка всех элементов
public void clear();
// Определение количества элементов в списке
public int countItems();
// Удаление элемента из заданной позиции
public void delItem(int position);
// Удаление нескольких элементов
public void delItems(int start, int end);
// Отмена выделения элемента с заданной позицией
public void deselect(int index);
// Получение строки, связанной с элементом, по
// позиции этого элемента
public String getItem(int index);
// Определение количества элементов, которые
// видны в окне списка
public int getRows();
// Определение номера выделенного элемента
public int getSelectedIndex();
// Определение номеров выделенных элементов
public int[] getSelectedIndexes();
// Получение текстовой строки, связанной с
// выделенным элементом
public String getSelectedItem();
// Получение ссылки на массив строк, связанных
// с выделенными элементами
public String[] getSelectedItems();
// Определение номера элемента массива, который
// был сделан в последний раз выделенным
// с помощью метода makeVisible
public int getVisibleIndex();
// Проверка, является ли выделенной
// строка с заданным номером
public boolean isSelected(int index);
// Выполняется свертка элементов списка таким
// образом, чтобы элемент с заданным номером
// стал видимым
public void makeVisible(int index);
// Минимальные размеры области, необходимые
// для отображения списка
public Dimension minimumSize();
// Минимальные размеры области, необходимые
// для отображения списка с заданным
// количеством строк
public Dimension minimumSize(int rows);
// Получение строки параметров
protected String paramString();
// Предпочтительные размеры области, необходимые
// для отображения списка
public Dimension preferredSize();
// Предпочтительные размеры области, необходимые
// для отображения списка с заданным
// количеством строк
public Dimension preferredSize(int rows);
// Извещение об уничтожении узла
public void removeNotify();
// Замещение элемента списка с заданным номером
public void replaceItem(String newValue, int index);
// Выделение элемента с заданным номером
public void select(int index);
// Установка или сброс режима одновременного
// выделения нескольких строк
public void setMultipleSelections(boolean v);
}
Процесс создания списка класса List несложен:
List chBackgroundColor;
chBackgroundColor = new List(6, false);
При создании списка вы передаете конструктору количество одновременно отображаемых строк и флаг разрешения одновременного выбора нескольких строк. Если значение этого флага равно true, пользователь сможет выбирать из списка одновременно несколько строк, а если false - только одну строку.
Для наполнения списка вы можете использовать уже знакомый вам метод addItem:
chBackgroundColor.addItem("Yellow");
chBackgroundColor.addItem("Green");
chBackgroundColor.addItem("White");
Список класса List добавляется к окну аплета методом add:
add(chBackgroundColor);
Кратко остановимся на нескольких методах класса List.
Если вы разрешили пользователю выбирать из списка одновременно несколько элементов, то для получения ссылки на массив выбранных элементов вам пригодятся методы getSelectedItems и getSelectedIndexes:
public String[] getSelectedItems();
public int[] getSelectedIndexes();
С помощью метода setMultipleSelections вы можете динамически включать или выключать режим одновременного выбора нескольких элементов.
В некоторых случаях вам может пригодиться метод clear, удаляющий все элементы из списка:
public void clear();
Методика использования других методов очевидна из краткого описания класса List, приведенного в этом разделе.
Определение атрибутов контекста отображения
Ряд методов класса Graphics позволяет определить различные атрибуты контекста отображения, например, цвет, выбранный в контекст отображения или метрики текущего шрифта, которым выполняется рисование текста.
Рассмотрим методы, позволяющие определить атрибуты контекста отображения.
Определение цвета, выбранного в контекст отображения
Метод getColor возвращает ссылку на объект класса Color, представляющий текущий цвет, выбранный в контекст отображения:
public abstract Color getColor();
Определение границ области ограничения вывода
С помощью метода clipRect, о котором мы расскажем чуть позже, вы можете определить в окне аплета область ограничения вывода прямоугольной формы. Вне этой области рисование графических изображений и текста не выполняется.
Метод getClipRect позволяет вам определить координаты текущей области ограничения, заданной в контексте отображения:
public abstract Rectangle getClipRect();
Метод возвращает ссылку на объект класса Rectangle, который, в частности, имеет поля класса с именами x, y, height и width. В этих полях находится, соответственно, координаты верхнего левого угла, высота и ширина прямоугольной области.
Определение класса
Для создания классов вы можете использовать только ключевое слово class. Что же касается union, то это ключевое слово в Java не применяется для создания классов.
В языке программирования С++ описание класса может быть отделено от его определения. Для Java это не так - описание класса не допустимо. Все методы должны быть определены внутри определения класса.
Недопустимо определение вложенных классов.
В Java также нет шаблонов. Вы можете создавать классы только на базе других классов.
Объект класса создается при помощи ключевого слова new, однако вы не можете удалить объект явным образом, так как ключевое слово delete языка программирования С++ в Java не используется.
При определении класса вы не можете указать деструктор. Функции удаления объектов Java из памяти выполняет система сборки мусора.
Внутри одного исходного файла вы можете определить только один общедоступный класс public.
Все классы в Java наследуются от класса Object, поэтому для любого объекта вы можете использовать методы этого класса.
Определение методов
Вы не можете определять методы вне тела класса, создавая таким образом глобальные функции. Нет также возможности определения вне класса глобальных данных. Тем не менее, внутри класса можно определять статические методы и поля (с помощью ключевого слова static), которые будут играть роль глобальных методов и данных.
Пользуясь ключевыми словами static и final, вы можете определять внутри классов глобальные константы.
Если в базовом классе метод определен с ключевым словом final, его нельзя переопределить в дочернем классе, созданном на базе данного метода.
Методы не могут быть определены как inline.
Методы Java могут создавать исключения, вызванные возникновением ошибок или других событий. Все создаваемые исключения должны либо обрабатываться внутри метода, либо описываться в определении метода после ключевого слова throws.
Определение метрик текущего шрифта
Несмотря на то что вы можете заказать шрифт с заданным именем и размером, не следует надеяться, что навигатор выделит вам именно такой шрифт, какой вы попросите. Для правильного размещения текста и других изображений в окне аплета вам необходимо знать метрики реального шрифта, выбранного навигатором в контекст отображения.
Метрики текущего шрифта в контексте отображения вы можете узнать при помощи метода getFontMetrics, прототип которого приведен ниже:
public FontMetrics getFontMetrics();
Метод getFontMetrics возвращает ссылку на объект класса FontMetrics. Ниже мы привели список наиболее важных методов этого класса, предназначенных для получения отдельных параметров шрифта:
Метод
Описание
public Font getFont();
Определение шрифта, который описывается данной метрикой
public int bytesWidth(
byte data[],
int off, int len);
Метод возвращает ширину строки символов, расположенных в массиве байт data. Параметры off и len задают, соответственно, смещение начала строки в массиве и ее длину
public int charsWidth(
char data[],
int off, int len);
Метод возвращает ширину строки символов, расположенных в массиве символов data. Параметры off и len задают, соответственно, смещение начала строки в массиве и ее длину
public int charWidth(
char ch);
Метод возвращает ширину заданного символа
public int charWidth(
int ch);
Метод возвращает ширину заданной строки символов
public int getAscent();
Определение расстояния от базовой линии до верхней выступающей части символов
public int getDescent();
Определение расстояния от базовой линии до нижней выступающей части символов
public int getLeading();
Расстояние между строками текста
public int getHeight();
Определение полной высоты символов, выполняется по формуле:
getLeading() + getAscent() + getDescent()
public int
getMaxAdvance();
Максимальная ширина символов в шрифте
public int getMaxAscent();
Максимальное расстояние от базовой линии до верхней выступающей части символов для символов данного шрифта
public int
getMaxDescent();
Максимальное расстояние от базовой линии до нижней выступающей части символов для символов данного шрифта
public int[] getWidths();
Массив ширин первых 256 символов в шрифте
public int
stringWidth(String str);
Ширина строки, передаваемой методу в качестве параметра
public String toString();
Тектовая строка, которая представляет данную метрику шрифта
Обратите внимание на метод stringWidth, позволяющий определить ширину текстовой строки. Заметим, что без этого метода определение ширины текстовой строки было бы непростой задачей, особенно если шрифт имеет переменную ширину символов.
Для определения полной высоты строки символов вы можете воспользоваться методом getHeight.
Определение метрик заданного шрифта
Метод getFontMetrics с параметром типа Font позволяет определить метрики любого шрифта, передаваемого ему в качетсве параметра:
public abstract FontMetrics getFontMetrics(Font f);
В отличие от нее метод getFontMetrics без параметров возвращает метрики текущего шрифта, выбранного в контекст отображения.
Определение шрифта, выбранного в контекст отображения
С помощью метода getFont, возвращающего ссылку на объект класса Font, вы можете определить текущий шрифт, выбранный в контекст отображения:
public abstract Font getFont();
Особенности реализации классов в Java
Если вы умеете программировать на языке С++, у вас не возникнет никаких проблем с программированием на Java, так как эти языки очень похожи. Однако есть и некоторые отличия, которые следует учитывать. Мы приведем только краткое перечисление основных отличий. Более подробную информацию вы найдете в литературе, список которой есть в конце книги.
Отпускание клавиши мыши
При отпускании клавиши мыши управление получает метод mouseUp:
public boolean mouseUp(Event evt, int x, int y);
Анализируя параметры x и y, вы можете определить координаты точки, в которой пользователь отпустил клавишу мыши.
Овалы и круги
Для рисования окружностей и овалов вы можете воспользоваться методом drawOval:
public abstract void drawOval(int x, int y,
int width, int height);
Параметры этого методы задают координаты и размеры прямоугольника, в который вписывается рисуемый овал (рис. 3.7).
Рис. 3.7. Рисование овала
Метод fillOval предназначен для рисования заполненного овала (рис. 3.8). Назначение его параметров аналогично назначению параметров метода drawOval:
public abstract void
fillOval(int x, int y, int width, int height);
Рис. 3.7. Рисование заполненного овала
Переключатели
Аплеты Java могут создавать в своем окне переключатели двух типов: с независимой фиксацией и с зависимой фиксацией.
Переключатели с независимой фиксацией имеют прямоугольную форму и, исходя из названия, работают независимо друг от друга. Если такой переключатель находится во включенном состоянии, внутри изображения маленького квадрата появляется галочка, если в выключенном - галочка исчезает.
Обычно переключатели с независимой фиксацией используются для управления независящими друг от друга режимами или параметрами.
Переключатели с зависимой фиксацией имеют круглую форму. В каждый момент времени может быть включен только один такой переключатель из группы переключателей с фиксацией. Аплет может создавать несколько групп переключателей с зависимой фиксацией.
Переключатели с зависимой фиксацией используются для выбора из нескольких взаимоисключающих возможностей, например, для установки одного из нескольких режимов.
Перемещение курсора мыши
Когда пользователь перемещает курсор мыши над окном аплета, в процессе перемещения происходит вызов метода mouseMove:
public boolean mouseMove(Event evt, int x, int y);
Через переменные x и y передаются текущие координаты курсора мыши.
Переопределение операторов
В языке С++ вы могли переопределить операторы, такие как +, -, ++ и так далее. Язык Java не допускает такое переопределение, что сделано для упрощения программирования. Тем не менее, операторы “+” и “+=” перегружены по умолчанию для выполнения операции слияния текстовых строк класса String.
Подготовка и запуск приложения
Итак, запустите среду Microsoft Developer Studio, сделав двойной щелчок левой клавишей мыши по соответствующей пиктограмме.
Выберите из меню File строку New. На экране появится диалоговая панель New, показанная на рис. 2.1.
Рис. 2.1. Диалоговая панель New
В этой диалоговой панели вам нужно выбрать строку Project Workspace и нажать кнопку OK. Сразу влсед за этим вы увидите диалоговую панлеь New Project Workspace (рис. 2.2).
Рис. 2.2. Диалоговая панель New Project Workspace
В левой части этой диалоговой панели есть список различных типов проектов, которые можно создать. К теме нашей книги относятся проекты типа Java Workspace и Java Applet Wizard. Первый из них предназначен для ручного создания приложений и аплетов Java, второй позволяет создавать заготовки аплетов Java в полуавтоматическом режиме.
Выберите проект типа Java Workspace. В поле Location укажите каталог, в котором будут создаваться проекты, а в поле Name - имя проекта. Переключатель Java Virtual Machine должен находиться во включенном состоянии.
После заполнения диалоговой панели New Project Workspace нажмите кнопку Create. Будет создан проект, в котором пока нет ни одного исходного файла.
Далее из меню Insert среды разработки Microsoft Developer Studio выберите строку Files into Project. В поле Filename наберите имя файла hello.java и нажмите кнопку Add. На экране появится сообщение о том, что файл с указанным именем не существует. Вы, однако, можете добавить ссылку на этот файл в проект, нажав кнопку Yes.
На следующем этапе вы должны открыть папку файлов проекта, отмеченную как hello files на средней странице блокнота, расположенного в левой части окна системы разработки (рис. 2.3).
Рис. 2.3. В проект добавлен файл hello.java
Сделайте двойной щелчок по имени файла hello.java. На экране появится сообщение о том, что файла с указанным именем не существует. Для того чтобы создать его, нажмите кнопку Yes.
В правой части главного окна системы разработки появится окно редактирования, в котором вам нужно ввести исходный текст нашей программы, приведенный в листинге 2.1.
Листинг 2.1. Файл hello\hello.java
public class hello
{
public static void main(String[] args)
{
System.out.println("Hello, Java!\n");
}
}
Затем выберите из меню Build строку Execute. На экране появится сообщение о том, что запускаемый файл hello.class не существует (рис. 2.4).
Рис. 2.4. Сообщение о том, что файл hello.class не существует
Для создания файла нажмите кнопку Yes. Исходный текст программы будет откомпиллирован. Если вы ввели его правильно, сообщения об ошибках не появятся.
Затем вы увидите на экране диалоговую панель Information For Running Class, показанную на рис. 2.5.
Рис. 2.5. Диалоговая панель Information For Running Class
В поле Class file name вам нужно ввести строку hello. Это имя класса и оно должно в точности соответствовать имени класса, указанному в определении класса (листинг 2.1), а также имени файла, в котором определен класс.
Заметим, что для каждого класса типа public вы должны создавать отдельный файл. Имя этого файла должно быть таким же, что и имя класса (с учетом строчных и прописных букв), а расширение имени файла необходимо указать как class.
Указав имя класса, включите переключатель Stand-alone interpreter в поле Run project under. При этом ваша программа будет выполняться под управлением автономного интерпретатора Java jview.exe, который находится в каталоге Windows.
После того как вы нажмете кнопку OK, ваше приложение будет запущено. На короткое время вы увидите окно интерпретатора Java, в котором появится сообщение Hello, Java! (рис. 2.6).
Рис. 2.6. Окно интерпретатора Java с сообщением, выведенным нашим приложением
Не огорчайтесь, что это окно быстро исчезло - вы можете запустить программу под отладкой и наслаждаться видом окна сколько угодно, остановив работу программы после оператора вывода сообщения. Давайте попробуем сделать это.
Установите курсор на оператор System.out.println("Hello, Java!\n") и нажмите комбинацию клавиш . Работа программы будет остановлена в момент достижения указанной строки. На экране появится окно интерпретатора jview.exe, не содержащее пока никаких сообщений. Далее выполняйте программу по шагам, нажимая клавишу . После первого раза в окне интерпретатора появится сообщение Hello, Java!. На второй раз приложение завершит свою работу и окно интерпретатора Java исчезнет с экрана.
Подключаемые библиотеки классов
Ниже мы кратко перечислим подлкючаемые библиотеки классов для того чтобы вы могли оценить возможности набора классов Java. Подробное описание этих классов есть в справочной системе Microsoft Visual J++ и в литературе, список которой приведен в конце книги. Мы же ограничимся описанием тех классов, которые будем использовать в наших примерах приложений.
Поля класса ButtonPress
Четыре поля класса Button с именами btn1, btn2, btn3 и btn4 предназначены для хранения ссылок на кнопки, размещенные в окне нашего аплета:
Button btn1;
Button btn2;
Button btn3;
Button btn4;
В поле sTextLabel класса String хранится строка, предназначенная для записи названия нажатой кнопки:
String sTextLabel;
Поля класса CheckBoxes
В нашем классе мы определили несколько полей, предназначенных для хранения ссылок на переключатели, группу переключателей и кнопку.
Ссылки на переключатели имеют тип Checkbox:
Checkbox chbox1;
Checkbox chbox2;
Checkbox chbox3;
Checkbox rdbox1;
Checkbox rdbox2;
Checkbox rdbox3;
Checkbox rdbox4;
Для того чтобы сгруппировать переключатели с зависимой фиксацией в группу, мы создали ссылку на объект класса CheckboxGroup:
CheckboxGroup grModeGroup;
Кроме того, нам потребуется ссылка на объект класса Button:
Button btnGet;
Поля класса ChoiceList
В нашем классе мы определили два поля для хранения ссылок на списки цвета фона и цвета изображения:
Choice chBackgroundColor;
Choice chForegroundColor;
Поля класса FrameWnd
В поле fMainFrame класса MainFrameWnd хранится ссылка на окно, которое будет создано, если пользователь нажмет кнопку “Show Frame Window”, расположенную в окне аплета. Класс MainFrameWnd создан нами на базе класса Frame.
Поля с именами btnShowFrame и btnHideFrame предназначены, соответственно, для хранения ссылок на только что указанную кнопку и кнопку “Hide Frame Window”, с помощью которой можно скрыть окно.
Поля класса KeyCode
Поле yHeight используется для хранения полной высоты символов текущего шрифта, выбранного в контекст отображения окна аплета. Эта величина нужна для определения шага свертки окна.
В поле dimAppWndDimension типа Dimension хранятся текущие размеры окна аплета.
Поля класса LineDraw
В нашем классе мы определили несколько полей, предназначенных для хранения текущих координат рисуемых линий.
В переменную dmDown класса Dimension записываются координаты курсора на момент нажатия клавиши мыши. Если пользователь нажал клавишу мыши для того чтобы приступить к рисованию линии, это будет координатами начала линии.
Когда пользователь отпускает клавишу мыши, координаты записываются в переменную dmUp.
В процессе рисования линии метод mouseDrag стирает ранее нарисованную линию и рисует новую. Координаты конца старой линии хранятся в переменной dmPrev.
Переменная bDrawing типа boolean хранит текущее состояние аплета. Когда аплет находится в состоянии рисования линии, в эту переменную записывается значение true, а когда нет - значение false.
И, наконец, переменная lines типа Vector является динамическим массивом, в котором хранятся координаты нарисованных линий.
Поля класса ListBox
В нашем классе мы определили два поля для хранения ссылок на списки цвета фона и цвета изображения, а также две строки для хранения названий выбираемых цветов:
List chBackgroundColor;
List chForegroundColor;
String sSelBackground = new String("Yellow");
String sSelForeground = new String("Black");
Содержимое строк sSelBackground и sSelForeground изменяется в процессе выделения пользователем различных строк списков.
Поля класса MainFrameWnd
В поле btnOK хранится ссылка на кнопку, при нажатии которой окно удаляется.
Поле mbMainMenuBar класса MenuBar предназначено для хранения ссылки на главное меню окна. В него мы будем добавлять меню “File” и “Help”, идентификаторы которых хранятся в полях mnFile и mnHelp, соответственно.
Поля класса MessageBox
Внутри диалоговой панели мы расположили текстовое поле класса Label, предназначенное для отображения сообщения, и кнопку с надписью OK, с помощью которой можно завершить работу диалоговой панели.
Ссылка на текстовое поле хранится в поле lbMsg, на кнопку - в поле btnOK.
Поля класса Notebook
В полях pDraw, pCardPanel и pButtonPanel находятся ссылки, соответственно, на верхнюю, среднюю и нижнюю панели, предназначенные для рисования, размещения блокнота диалоговых панелей настроек и кнопок управления блокнотом.
В предыдущем приложении на страницах блокнота размещались кнопки. Теперь мы решили более сложную задачу - поместили на страницы блокнота три панели, по одной на каждую страницу. Первая из этих панелей содержит список для выбора цвета фона, вторая - для выбора цвета изображения и, наконец, третья, для выбора шрифта. Поля pBackgroundColor, pForegroundColor и pFont хранят ссылки на соответствующие панели настроек.
Нижняя панель содержит кнопки управления страницами блокнота. С помощью кнопок, ссылки на которые хранятся в полях btnBackgroundColor, btnForegroundColor и btnFont вы можете выбирать для отображения страницы блокнота, содержащие панели настройки цвета фона, изображения и шрифта. Таким образом, нет необходимости перебирать страницы блокнота по очереди до тех пор, пока в окне не появится нужная страница. Тем не менее, мы предусмотрели кнопки и для циклического перебора страниц блокнота. Ссылки на эти кнопки хранятся в полях btnNext и btnPrev.
На каждой панели в блокноте размещается один список и одна надпись, объясняющая назначение списка. Списки создаются как объекты класса Choice, а надписи - как объекты класса Label.
Поля chBackgroundColor, chForegroundColor и chFont хранят ссылки на списки, соответственно, цвета фона, цвета изображения и шрифтов. В полях tBackgroundColor, tForegroundColor и tFont хранятся ссылки надписей.
Поле sFontName класса String предназначено для хранения названия текущего шрифта, с использованием которого отображается текст в верхней панели.
В классе Panel2 определено два
В классе Panel2 определено два поля с именами pPanel1 и pPanel2 класса Panel. Первое из них предназначено для хранения ссылки на верхюю панель, второе - на нижнюю (в соответствии с их расположением в окне аплета).
Поля класса PanelDemo
В поле pCardPanel хранится ссылка на панель блокнота, страницы которого содержать кнопки. Эта панель располагается в верхней части окна аплета.
Поле pButtonPanel предназначено для хранения ссылки на панель кнопок, предназначенных для перелистывания страниц блокнота pCardPanel.
Ссылки на кнопки, расположенные на страницах блокнота, хранятся в полях btn1, btn2, btn3, btn4 и btn5.
Ссылки на кнопки, предназначенные для перелистывания страниц блокнота, записываются в поля btnNext и btnPrev (соответственно, кнопка пролистывания блокнота в прямом и обратном направлении).
В поле sTextLabel хранится название нажатой кнопки. Это название отображается в строке состояния навигатора, когда пользователь нажимает какую-либо кнопку в верхней или нижней панели.
Поля класса TextLabel
Мы определили четыре поля - lbTextLabel, btnLeft, btnCenter и btnRight:
Label lbTextLabel;
Button btnLeft;
Button btnCenter;
Button btnRight;
Первое из них предназначено для хранения ссылки на объект класса Label (однострочное текстовое поле), остальные три - для хранения ссылок на кнопки, определяющие выравнивание.
Поля класса TextOut
Прежде всего, создаются поля класса для хранения значений параметров:
private String m_Str1 = "Hello 1";
. . .
private String m_Str5 = "Hello 5";
private String m_Font1 = "Arial";
. . .
private String m_Font5 = "Undefined";
private String m_Type1 = "Bold";
private String m_Type2 = "Italic";
private String m_Type3 = "Plain";
Поля инициализируются значениями по умолчанию, которые вы ввели при заполнении таблицы, показанной на рис. 3.13.
Далее в классе определяются поля с названиями параметров:
private final String PARAM_Str1 = "Str1";
. . .
private final String PARAM_Str5 = "Str5";
private final String PARAM_Font1 = "Font1";
. . .
private final String PARAM_Font5 = "Font5";
private final String PARAM_Type1 = "Type1";
private final String PARAM_Type2 = "Type2";
private final String PARAM_Type3 = "Type3";
Названия параметров будут нужны для извлечения значений параметров методом getParameter класса Applet.
Поля класса TxtField
В поле txt хранится ссылка на объект класса TextField - наше однострочное поле редактирования:
TextField txt;
В полях btnGetText и str хрянятся, соответственно, ссылки на кнопку и текстовую строку, в которую записывается текущее содержимое поля редактирования:
Button btnGetText;
String str;
В поле txt хранится ссылка на объект класса TextArea - многострочное поле редактирования:
TextArea txt;
В полях btnGetText, btnGetSelectedText и str хрянятся, соответственно, ссылки на кнопки и текстовую строку, в которую записывается текущее содержимое поля редактирования:
Button btnGetText;
Button btnGetSelectedText;
String str;
Полотно для рисования
Проще всего представить себе контекст отображения как полотно, на котором рисует художник. Точно так же как художник может выбирать для рисования различные инструменты, программист, создающий аплет Java, может выбирать различные методы класса Graphics и задавать различные атрибуты контекста отображения.
Приложение Border
В приложении Border создается пять кнопок, которые размещаются в режиме BorderLayout (рис. 6.3).
Рис. 6.3. Окно аплета Border
Заметьте, что в процессе размещения северная и южная кнопка (верхняя и нижняя на нашем рисунке) заняли по ширине все окно аплета. Высота же этих кнопок была установлена достаточной для размещения одной строки текста.
Восточная и западная кнопка (левая и правая) имеют ширину, достаточную для размещения текста, и высоту, равную высоте области, которая осталась от сервеной и южной кнопок.
Для центральной кнопки было выделено все оставшееся пространство в центре окна аплета.
Приложение ButtonPress
В окне приложения ButtonPress мы создаем четыре кнопки с названиями от Button 1 до Button 4. Когда пользователь нажимает на одну из кнопок, название нажатой кнопки отображается в окне аплета и в строке состояния навигатора (рис. 5.3).
Рис. 5.3. Кнопки в окне аплета ButtonPress
Обратите внимание на расположение кнопок. По мере добавления, кнопки располагаются по горизонтали справа налево и центрируются в окне аплета. Если бы ширины окна аплета не хватило для размещения четырех кнопок, не поместившиеся кнопки были бы нарисованы ниже. Такую стратегию размещения выбирает по умолчанию система Layout Manager класса Panel, от которого, как вы знаете, произошел класс Applet.
Приложение CheckBoxes
Для демонстрации методов работы с различными переключателями мы подготовили приложение CheckBoxes. Окно соответствующего аплета показано на рис. 5.4.
Рис. 5.4. Окно аплета CheckBoxes с переключателями и кнопкой
В верхней части окна располагаются четыре переключателя с зависимой фиксацией, принадлежащих к одной группе. Ниже находятся три переключателя с независимой фиксацией, а еще ниже - кнопка с надписью Get CheckBoxes state. Нажав на эту кнопку, вы можете увидеть в нижней части окна аплета список включенных переключателей.
Одновременно может быть включен только один из переключателей Mode1 - Mode 4, так как эти переключатели составляют группу переключателей с зависимой фиксацией. Переключатели Switch 1, Switch 2 и Switch 3 могут находиться в произвольном состоянии независимо друг от друга.
Заметим, что переключатели и кнопка размещались в окне аплета автоматически по мере добавления. Если бы мы добавляли эти компоненты в другой последовательности или если бы окно аплета имело другие размеры, то переключатели могли бы не оказаться сгруппированными, как это показано на рис. 5.4. Позже в этой главе мы научим вас настраивать систему Layout Manager таким образом, чтобы вы смогли располагать компоненты в заданном вами порядке с предсказуемым результатом.
Приложение ChoiceList
В прложении ChoiceList мы создали два списка, первый из которых управляет цветом фона окна аплета, а второй - цветом изображения, то есть цветом, которым рисуется изображение в этом окне (рис. 5.7).
Рис. 5.7. Окно аплета ChoiceList, в котором создано два списка класса Choice
Пользоваться этим аплетом очень просто - выбирайте из левого списка цвет фона, а из правого - цвет изображения, при этом следите за цветом, которым нарисована текстовая строка и рамка вокруг окна аплета.
Приложение FontList
Наше следующее приложение отображает список шрифтов, доступных в системе. Окно аплета этого приложения, запущенного в среде Microsoft Internet Explorer, работающего в Microsoft Windows NT версии 4.0, показано на рис. 3.10.
Рис. 3.10. Окно аплета со списком доступных шрифтов
Возможно, этот список покажется вам несколько необычным. В самом деле, давайте заглянем в папку Fonts, которую можно найти в папке Control Panel. Беглого взгляда достаточно для того, чтобы убедиться - список шрифтов, доступных аплету, не совпадает со списком шрифтов, установленных в системе (рис. 3.11).
Рис. 3.11. Список шрифтов, установленных в системе Microsoft Windows NT
Задавая имена шрифтов в конструкторе класса Font, вы должны использовать имена шрифтов, доступные аплету, а не имена шрифтов, установленных в системе. Наш аплет FontList извлекает и отображает список доступных для него шрифтов.
Приложение FrameWnd
В приложении FrameWnd мы демонстрируем создание окон, меню и диалоговых панелей на базе классов, описанных в этой главе.
В окне аплета FrameWnd расположены две кнопки с названиями Show Frame Window и Hide Frame Window. Первая из них предназначена для отображения окна Main Frame Window, а вторая - для его временного удаления (скрытия).
В окне Main Frame Window мы создали главное меню, содержащее меню File и Help. При выборе любой строки из этого меню, кроме строки Exit меню File, на экране появляется окно диалоговой панели Dialog from Frame с названием выбранной строки меню (рис. 8.2).
Рис. 8.2. Окно и диалоговая панель, создаваемая аплетом FrameWnd
Помимо меню, в окне Main Frame Window находится кнопка OK, нажатие на которую вызывает удаление окна. Кроме того, в нижней части окна отображается строка “Окно класса Frame”.
В окне диалоговой панели, разделенном по вертикали на две части, находится текстовое поле для отображения сообщения и кнопка для завершения работы диалоговой панели.
Обратите также внимание на то, что в самой нижней части окно Main Frame Window и Dialog from Frame находится предупреждающее сообщение “Warning: Applet Window”. Это предупреждение пользователю, что данное окно или диалоговая панель выведена не локальным приложением, запущенным на компьютере пользователя, а аплетом, загруженным из сети. Пользователь должен понимать, что данные, введенные им в окнах, созданных аплетами, передаются по сети и могут оказаться доступной кому угодно. Поэтому он не должен вводить конфиденциальную информацию, например, номера своих кредитных карточек.
Приложение Grid
Приложение Grid демонстрирует использование режима размещения компонент GridLayout. В окне аплета мы создали таблицу размером 3х3, разместив в ее ячейках восемь кнопок (рис. 6.2).
Рис. 6.2. Окно приложения Grid
Обратите внимание, что все кнопки в окне аплета имеют одинаковый размер. Если вы станете нажимать на них, в строке состояния навигатора будет появляться название нажатой кнопки (на рис. 6.2 не показано).
Приложение Hello
Как мы уже говорили, приложения Java могут выполняться под управлением специального интерпретатора, работающего в рамках отдельного процесса, либо под управлением навигатора Internet, такого как Microsoft Internet Explorer или Netscape Navigator. В последнем случае приложение называется аплетом.
Первое приложение, которое мы рассмотрим, относится к простым приложениям, второе будет аплетом, встроенным в документ HTML.
Приложение KeyCode
Для демонстрации методов обработки клавиатурных событий мы подготовили аплет KeyCode. В его окне отображаются символы, соответствующие нажимаемым клавишам, код соответствующих клавиш и коды модификации (рис. 4.4).
Рис. 4.4. Окно аплета KeyCode
Аплет KeyCode интересен тем, что в процессе отображения новые строки появляются в верхней части окна, а старые сдвигаются вниз после отпускания клавиши. Таким образом, мы организовали свертку в окне аплета.
Прежде чем нажимать клавиши, вы должны передать фокус вода окну аплета. Это можно сделать, щелкнув в окне левой клавишей мыши. Фокус ввода - это атрибут, который присваивается окну, обрабатывающему ввод от клавиатуры. Так как клавиатура одна, а аплетов и других активных окон на экране может быть много, необходим механизм, позволяющий определить, в какое окно направялются события, создаваемые клавиатурой. Такой механизм и обеспечивается атрибутом фокуса ввода.
Приложение LineDraw
Следующее приложение тоже работает с мышью. В его окне вы можете рисовать прямые линии черного цвета (рис. 4.3).
Рис. 4.3. Аплет, в окне которого можно рисовать прямые линии
Для того чтобы нарисовать линию в окне аплета LineDraw, вы должны установить курсор в начальную точку, нажать клавишу мыши и затем, не отпуская ее, переместить курсор в конечную точку. После отпускания клавиши мыши координаты линии будут сохранены аплетом в массиве, после чего произойдет перерисовка окна аплета.
По мере того как вы будете рисовать линии, аплет будет заполнять массив с координатами линий. Каждый раз, когда окно аплета будет перерисовываться, метод paint перерисует все линии заново, пользуясь координатами, сохраненными в массиве.
Для того чтобы стереть содержимое окна аплета, вам достаточно сделать двойной щелчок в его окне. При этом из массива координать линий будут удалены все элементы.
Приложение ListBox
В окне приложения ListBox мы создали два списка класса List, первый из которых предназначен для выбора цвета фона, а второй - для выбора цвета изображения. Размер первого списка достаточен для отображения всех добавленных в него элементов. Размер второго списка специально сделан меньше, поэтому справа от него появилась полоса просмотра (рис. 5.10).
Рис. 5.10. Окно приложения ListBox
Если вы будете выделять различные строки списков одинарным щелчком клавиши мыши, в нижней части окна и строке состояния навигатора (на рисунке не показана) появятся названия выделенных цветов. Таким образом, аплет отслеживает выделение элементов этих двух списков, отображая связанные с ним текстовые строки.
В том случае, если вы сделаете двойной щелчок мышью внутри одного из списков, соответствующим образом изменится цвет фона или цвет изображения (текстовых строк и рамки вокруг окна аплета).
Приложение MouseClick
Аплет MouseClick демонстрирует обработку событий, поступающих от мыши.
Когда мы создавали проект этого аплета, то в третьей диалоговой панели системы Java Applet Wizard включили три переключателя в поле Which mouse event handlers would you like added (рис. 4.1).
Рис. 4.1. Включение обработчиков событий от мыши
В результате в исходный текст приложения были добавлены перечисленные выше методы, обрабатывающие события, создаваемые мышью.
Мы изменили эти методы таким образом, чтобы в ответ на различные действия, выполняемые пользователем с помощью мыши, аплет реагировал соответствующим образом:
Действие пользователя
Реакция аплета
Перемещение курсора мыши при отжатой клавише
Игнорирование
Перемещение курсора мыши при нажатой клавише
В строку состояния записывается текстовая строка Mouse Drag
Нажатие клавиши мыши
В месте расположения курсора выводятся текущие координаты курсора мыши. Дополнительно в строку состояния записывается текстовая строка Mouse Down
Отжатие клавиши мыши
В строку состояния записывается текстовая строка Mouse Up
Курсор мыши входит в область окна аплета
В строку состояния записывается текстовая строка Mouse pointer enters applet's window
Курсор мыши выходит из области окна аплета
В строку состояния записывается текстовая строка Mouse pointer leaves applet's window
Внешний вид окна аплета, в котором отображаются координаты курсора, показан на рис. 4.2.
Рис. 4.2. Внешний вид окна аплета MouseClick, в котором отображаются координаты курсора
Приложение Notebook
Приложение Notebook служит в качестве более сложного примера техники работы с панелями.
В окне аплета Notebook создаются три панели, расположенные в одном столбце. В верхней панели, имеющей рамку по периметру, рисуется строка текста “Смотри на шрифт, цвет фона и текста!”. Средняя панель представляет собой блокнот, предназначенный для выбора цвета фона, цвета изображения и шрифта для верхней панели. И, наконец, нижняя панель содержит кнопки, позволяющие перелистывать страницы блокнота.
На рис. 7.4 показана страница, предназанченная для выбора цвета фона:
Рис. 7.4. Страница, предназанченная для выбора цвета фона
Нажимая кнопки Background Color, Foreground Color и Set Text Font, вы сможете выдвигать на передний план страницы блокнота, предназначенные, соответственно, для выбора цвета фона, изображения и шрифта, которым отображается текст в верхней панели.
Кнопки Next и Prev работают таким же образом, что и в предыдущем приложении, а именно: если нажать на кнопку Next, произойдет пролистывание страниц в прямом направлении, а если на кнопку Prev - в обратном направлении.
На рис. 7.5 изображена страница блокнота, предназначенная для выбора цвета изображения.
Рис. 7.5. Страница, предназанченная для выбора цвета изображения
На рис. 7.6 представлена страница, с помощью которой можно выбрать один из нескольких шрифтов для рисования текста в верхней панели.
Рис. 7.6. Страница, предназанченная для выбора шрифта
Рассмотрим исходные тексты приложения Notebook/
Приложение Painter
В этом разделе мы рассмотрим исходные тексты аплета Painter, в которых демонстрируется использование большинства только что описанных нами функций рисования.
Внешний вид окна аплета при просмотре соответствующего документа HTML навигатором Microsoft Internet Explorer показано на рис. 3.9.
Рис. 3.9. Окно аплета Painter
Здесь мы написали текстовую строку, нарисовали несколько горизонтальных линий, скопировав это линии в другое место окна, а также изобразили несколько простейших геометрических фигур.
В окне аплета Panel2 мы
В окне аплета Panel2 мы создали две панели, одна из которых занимает верхнюю половину окна, а другая - нижнюю (рис. 7.7).
Рис. 7.7. Окно аплета Panel2 с двумя панелями
Для каждой панели мы создали два отдельных класса на базе класса Panel. В созданных нами классах переопределен метод paint, который рисует одну текстовую строку в окне своей панели. В верхней панели рисуется строка “Первая панель”, в нижней - “Вторая панель”.
Кроме того, метод paint класса нашего аплета рисует две строки в окнах обеих панелей, получая контекст отображения для панелей и указывая ссылку на этот контекст явным образом.
В процессе инициализации метод init класса нашего аплета устанавливает различный цвет фона и изображения для панелей, поэтому рамки вокруг окон панелей и текст получаются нарисованными различным цветом независимо от способа их рисования.
Приложение PanelDemo
В приложении PanelDemo мы создаем две панели, расположенные горизонтально. Первая из них используется как блокнот, на каждой странице которого находится кнопка, вторая содержит две управляющие кнопки, позволяющие перебирать страницы блокнота по очереди (рис. 7.2).
Рис. 7.2. Окно аплета PanelDemo
Объемное изображение схемы расположения панелей и кнопок относительно окна аплета показано на рис. 7.3.
Рис. 7.3. Объемное изображение схемы расположения панелей и кнопок
В верхней панели друг над другом располагаются пять кнопок (как колода карт), причем видна только одна из них. В нижней панели только две кнопки, с помощью которых можно выдвигать на передний план по очереди все кнопки из верхней панели.
Нажимая на кнопки Next и Prev, попробуйте понажимать на кнопки в верхней панели. В строке состояния навигатора при этом будет отображаться название нажатой кнопки (на рис. 6.5 это не показано).
Приложение TextEdit
Приложение TextEdit (рис. 5.13) демонстрирует некоторые приемы работы с многострочным полем редактирования текста, созданным на базе класса TextArea.
Рис. 5.13. Окно приложения TextEdit
В окне редактирования вы можете вводить строки текста. Если нажать на кнопку “Получить все”, в нижней части окна отобразится полное содержимое окна редактирования. Каждая строка будет отделена символом перехода на новую строку.
Если же нажать кнопку “Получить выделенное”, в нижней части появится только выделенный фрагмент текста (как это показано на рис. 5.13).
Приложение TextLabel
В окне приложения TextLabel, демонстрирующего способы работы с полями класса Label, мы разместили одно такое поле и три кнопки, позволяющие изменять выравнивание текста в поле (рис. 5.11).
Рис. 5.11. Окно приложения TextLabel
Нажимая кнопки, вы можете заметить, как строка, расположенная под ними, немного сдвигается по горизонтали.
Приложение TextOut
До сих пор наши аплеты не получали параметров из документов HTML, в которые мы их встраивали. Конечно, все константы, текстовые строки, адреса URL и другую информацию можно закодировать непосредственно в исходном тексте аплета, однако, очевидно, это очень неудобно.
Пользуясь операторами , расположенными в документе HTML сразу после оператора , можно передать аплету произвольное количество параметров, например, в виде текстовых строк:
code=TextOut.class
id=TextOut
width=320
height=240 >
. . .
Здесь через параметр NAME оператора передается имя параметра аплета, а через параметр VALUE - значение соответствующего параметра.
Как параметр может получить значение параметров?
Для получения значения любого параметра аплет должен использовать метод getParameter. В качестве единственного параметра этому методу передается имя параметра аплета в виде строки типа String, например:
private String m_ParamName1;
private final String PARAM_ ParamName1= "ParamName1";
String param;
param = getParameter(PARAM_ParamName1);
if (param != null)
m_ParamName1 = param;
Если вы создаете аплет с помощью системы Java Applet Wizard, то в четвертой диалоговой панели вам предоставляется возможность определить все параметры, передаваемые аплету (рис. 3.12).
Рис. 3.12. Определение параметров аплета (список параметров уже заполнен)
Первоначально список параметров, отображаемых в четвертой диалоговой панели системы Java Applet Wizard, пуст. Такую панель мы показывали раньше на рис. 2.11.
Для добавления нового параметра сделайте щелчок левой клавишей мыши в столбце Name по свободному полю, отмеченному пунктирным прямоугольником. На месте этого прямоугольника появится поле редактирования, в котором вы должны ввести имя параметра. После ввода сделайте щелчок вне поля, после чего в списке параметров появится новая строка.
Создавая проект TextOut, мы выполнили эту операцию для всех параметров, за исключением параметра Font4. Этот параметр мы добавили позже в ручном режиме, когда все файлы проекта уже были созданы.
Обратите внимание, что в столбце Member при заполнении списка автоматически появляются имена полей класса, в которые попадут значения параметров.
После завершения формирования списка параметров мы заполнили столбцы Def-Value и Description (рис. 3.13).
Рис. 3.13. Заполнение столбцов Def-Value и Description
Значения из столбца Def-Value будут использованы для инициализации соответствующих полей класса. Что же касается столбца описаний Description, о эта информация может быть извлечена аплетом и проанализирована. Если в документе HTML находится несколько аплетов (что вполне допустимо), другие аплеты также могут получить описание параметров нашего аплета.
Какие параметры получает наш аплет и что он делает, кроме получения значения параметров?
Через параметры с именами Str1 - Str5 передается пять строк, который аплет отображает в своем окне (рис. 3.14).
Рис. 3.14. Отображение строк в окне аплета TextOut
Параметры Font1 - Font5 задают имена шрифтов для отображения этих строк. С помощью параметра Type1 можно задать стиль шрифта первой и второй строки, с помощью параметра Type2 - третьей и четвертой, а с помощью параметра Type3 - стиль шрифта для пятой строки.
Рассмотрим исходный тексты приложения TextOut.
Приложение TxtField
В приложении TxtField мы создали однострочное поле редактирования на базе класса TextField и кнопку, с помощью которой можно извлечь текст из поля для отображения (рис. 5.12).
Рис. 5.12. Окно аплета TxtField
Изменив текст в поле редактирования, нажмите кнопку “Получить строку”. В нижней части окна аплета вы увидите измененный вами текст.
Прямоугольники и квадраты
Среди методов класса Graphics есть несколько, предназначенных для рисования прямоугольников. Первый из них, с именем drawRect, позволяет нарисовать прямоугольник, заданный координатами своего левого вернего угла, шириной и высотой:
public void drawRect(int x, int y,
int width, int height);
Параметры x и y задают, соответственно, координаты верхнего левого угла, а параметры width и height - высоту и ширину прямоугольника (рис. 3.2).
Рис. 3.2. Рисование прямоугольника
В отличие от метода drawRect, рисующего только прямоугольную рамку, метод fillRect рисует заполненный прямоугольник. Для рисования и заполнения прямоугольника используется цвет, выбранный в контекст отображения (рис. 3.3).
Прототип метода fillRect приведен ниже:
public abstract void
fillRect(int x, int y, int width, int height);
Рис. 3.3. Рисование заполненного прямоугольника
Метод drawRoundRect позволяет нарисовать прямоугольник с закругленными углами:
public abstract void
drawRoundRect(int x, int y, int width,
int height, int arcWidth, int arcHeight);
Параметры x и y определяют координаты верхнего левого угла прямоугольника, параметры width и height задают, соответственно его ширину и высоту.
Размеры эллипса, образующего закругления по углам, вы можете задать с помощью параметров arcWidth и arcHeight. Первый из них задает ширину эллипса, а второй - высоту (рис. 3.4).
Рис. 3.4. Рисование прямоугольника с закругленными углами
Метод fillRoundRect позволяет нарисовать заполненный прямоугольник с закругленными углами (рис. 3.5). Назначение параметров этого метода аналогично назначению параметров только что рассмотренного метода drawRoundRect:
public abstract void
fillRoundRect(int x, int y, int width, int height,
int arcWidth, int arcHeight);
Рис. 3.5. Рисование заполненного прямоугольника с закругленными углами
Метод fill3Drect предназначен для рисования выступающего или западающего прямоугольника:
public void
fill3DRect(int x, int y, int width,
int height, boolean raised);
Если значение параметра raised равно true, рисуется выступающий прямоугольник, если false - западающий. Назначение остальных параметров аналогично назначению параметров метода drawRect.
Простейший аплет
Аплетами называются приложения Java, которые выполняются под управлением виртуальной машины Java, встроенной в навигатор, такой как Microsoft Internet Explorer или Netscape Navigator. Аплет встраивается в документ HTML и выглядит как окно заранее заданного размера. Он может рисовать в своем окне (и только в нем) произвольные изображения или текст.
Двоичный файл с исполняемым (а точнее говоря, интерпретируемым) кодом Java располагается на сервере WWW. В документе HTML с помощью оператора организуется ссылка на этот двоичный файл.
Когда пользователь загружает в навигатор документ HTML с аплетом, файл аплета переписывается с сервера WWW на рабочую станцию пользователя. После этого навигатор начинает его выполнение.
Возможно, вам не понравится такая идея, как запуск чужого аплета на своем компьютере - мало ли чего этот аплет может там сделать. Однако аплеты, в отличие от обычных приложений Java, сильно ограничены в своих правах. Например, они не могут читать локальные файлы и тем более в них писать. Есть также ограничения и на передачу данных через сеть: аплет может передавать данные только тому серверу WWW, с которого он загружен. В крайнем случае вы можете совсем отказаться от использования аплетов, отключив возможность их загрузки соответствующей настройкой навигатора. Но мы пока не будем этого делать, так как аплеты являются предметом изучения в нашей книге.
Давайте создадим простейший аплет, воспользовавшись для этого системой автоматизированной разработки шаблонов аплета Java Applet Wizard, встроенной в Microsoft Visual J++.
Запустите систему Microsoft Visual J++ и выберите из меню File строку New. В появившейся на экране диалоговой панели New выберите строку New Project Workspace. Затем вам нужно выбрать тип проекта Java Applet Wizard, как это показано на рис. 2.7.
Рис. 2.7. Выбор типа проекта Java Applet Wizard
В поле Name введите имя приложения HelloAp, а в каталоге Location укажите путь к каталогу, в котором будут созданы файлы проекта. Затем нажмите кнопку Create. Вслед за этим на экране появится по очереди несколько диалоговых панелей, в которых вы должны описать создаваемый аплет.
Первая такая диалоговая панель показана на рис. 2.8.
Рис. 2.8. Выбор типа приложения, названия класса и степени подробности создаваемых комментариев к исходному тексту
В поле How would you like to be able to run your program включите переключатель As an applet only. При этом создаваемое приложение сможет работать только под управлением навигатора.
Имя класса аплета нужно указать в поле What would you like to name your applet class. Оставьте имя HelloAp, которое там есть по умолчанию.
Состояние переключателей в поле Would you like to generate source file comments влияет на то, насколько подробно будут комментироваться создаваемый исходный текст приложения, и будет ли он комментироваться вообще.
Если включить переключатель Yes, please, в исходный текст будут добавлены комментарии. Если же включить переключатель No, thank you, никаких комментариев не будет.
При включении переключателя Explanatory comments в исходный текст будут включены комментарии, объясняющие назначение отдельных фрагментов кода. Переключатель TODO влияет на то, будут ли отмечены места исходного текста, в который вы должны вставить свой код, наполняющий аплет реальной жизнью.
Завершив заполнение первой диалоговой панели, нажмите кнопку Next и переходите к следующей панели, показанной на рис. 2.9.
Рис. 2.9. Запрос на создание документа HTML и определение размера окна аплета
Система Java Applet Wizard может создать для вас образец документа HTML, в который будет включен разрабатываемый вами аплет. Для этого во второй диалоговой панели вы должны включить переключатель Yes, please, расположенный в поле Would you like a sample HTML file.
Начальные размеры окна, создаваемого в документе HTML для аплета, определяются в полях Width in pixels и Height in pixels (соответственно, ширина и высота). Заметим, что аплет может изменять размеры своего окна, о чем мы еще будем говорить.
Третья диалоговая панель показана на рис. 2.10.
Рис. 2.10. Вопросы, связанные с мультизадачностью, анимацией и обработкой сообщений от мыши
В этой панели вы должны указать, будет ли ваш аплет создавать задачи. Наш первый аплет однозадачный, поэтому в поле Would you like your applet to be multi-threaded вы должны вклюить переключатель No, thank you.
На вопрос Would you like support for animation вы сможете ответить утвердительно только в том случае, если ваш аплет мультизадачный.
Три переключателя, расположенные в поле Which mouse event handlers would you like added, позволят вам автоматически добавить обработчики сообщений от мыши. Пока не включайте их, так как мышью мы займемся позже.
Следующая, четвертая диалоговая панель показана на рис. 2.11.
Рис. 2.11. Определение параметров, передаваемых аплету
С помощью этой диалоговой панели вы можете указать, какие параметры должны передаваться аплету через документ HTML при запуске. Нажмите здесь кнопку Next, не добавляя никаких параметров.
В пятой диалоговой панели (рис. 2.12) вам дается возможность отредактировать информацию, описывающую ваш аплет.
Рис. 2.12. Редактирование информации, описывающей аплет
Эта информация будет возвращаться методом getAppInfo, определенным в классе аплета. При необходимости измените строки описания и нажмите кнопку Next.
Финальная диалоговая панель показана на рис. 2.13.
Рис. 2.13. Финальная диалоговая панель
Здесь вы можете последний раз перед созданием файлов проекта посмотреть на заданные вами параметры. Если нажать кнопку OK, проект будет создан. Для отказа от создания проекта нажмите кнопку Cancel.
В результате работы системы Java Applet Wizard будет создано два файла (не считая файла проекта). Это исходный текст аплета HelloAp.java (листинг 2.2) и исходный текст документа HTML HelloAp.html, в который включен создаваемый аплет (листинг 2.3).
Листинг 2.2. Файл HelloAp\HelloAp.java (комментарии переведены на русский язык)
//**********************************************************
// HelloAp.java: Applet
//
//**********************************************************
import java.applet.*;
import java.awt.*;
//==========================================================
// Основной класс для аплета HelloAp
//
//==========================================================
public class HelloAp extends Applet
{
// Конструктор класса HelloAp
//---------------------------------------------------
public HelloAp()
{
// Сделать: Добавьте сюда код конструктора
}
// Обеспечение информации об аплете:
//
// Метод getAppletInfo возвращает строку, которая
// описывает аплет. Вы можете приведсти такую информацию,
// как имя автора и дата создания, а так же любые другие
// сведения об аплете
//------------------------------------------------------
public String getAppletInfo()
{
return "Name: HelloAp\r\n" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// Метод init вызывается системой AWT при первой загрузке
// или перезагрузке аплета. Вы можете переопределить этот
// метод для выполнения еобходимой инициализации аплета,
// например, инициализации структур данных, загрузку
// изображений или шрифтов, создание окон фреймов,
// установку системы управления внешним видом или
// добавление элементов пользовательского интерфейса
//------------------------------------------------------
public void init()
{
// Если для размещения в окне аплета органов управления
// вы используете класс "control creator", созданный
// системой ResourceWizard, из метода init можно
// вызывать метод CreateControls. Удалите вызов функции
// resize перед добавлением вызова функции
// CreateControls, так как эта функция выполняет
// изменение размера окна аплета самостоятельно
//--------------------------------------------------
resize(320, 240);
// Сделать: Добавьте сюда дополнительный код
// инициализации
}
// Разместите здесь дополнительный код, необходимый
// для “чистого” завершения работы аплета. Метод
// destroy вызывается, когда аплет завершает работу
// и будет выгружен из памяти
//---------------------------------------------------
public void destroy()
{
// Сделать: Добавьте сюда код завершения работы аплета
}
// Обработчик процедуры рисования окна аплета HelloAp
//---------------------------------------------------
public void paint(Graphics g)
{
g.drawString(
"Created with Microsoft Visual J++ Version 1.0",
10, 20);
}
// Метод start вызывается при первом появлении на
// экране страницы HTML с аплетом
//---------------------------------------------------
public void start()
{
// Сделать: Добавьте сюда дополнительный код,
// который должен работать при запуске аплета
}
// Метод stop вызывается когда страница HTML с
// аплетом исчезает с экрана
//---------------------------------------------------
public void stop()
{
// Сделать: Добавьте сюда дополнительный код,
// который должен работать при остановке аплета
}
// Сделать: Добавьте сюда дополнительный код
}
Листинг 2.3. Файл HelloAp\HelloAp.html
HelloAp
code=HelloAp.class
id=HelloAp
width=320
height=240 >
The source.
Режим BorderLayout
При использовании режима BorderLayout окно контейнера разделяется на рамку и центральную часть. При размещении компонент указывается направление от центра окна, в котором слудует размещать компоненты.
Ниже приведено краткое описание класса BorderLayout:
public class java.awt.BorderLayout
extends java.lang.Object
implements java.awt.LayoutManager
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public BorderLayout();
public BorderLayout(int hgap, int vgap);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
public void addLayoutComponent(String name,
Component comp);
public void layoutContainer(Container target);
public Dimension minimumLayoutSize(Container target);
public Dimension preferredLayoutSize(Container target);
public void removeLayoutComponent(Component comp);
public String toString();
}
Два конструктора предназначены для создания схемы размещения, соответственно, без зазора между компонентами и с зазором заданной величины.
Добавляя компоненты к контейнеру, вы должны использовать метод add с двумя параметрами, первый из которых указывает направление размещения, а второй - ссылку на добавляемый объект:
add("North", btn1);
add("East", btn2);
add("West", btn3);
add("South", btn4);
add("Center", btn5);
Режим CardLayout
Режим CardLayout предназначен для создания набора диалоговых панелей, которые можно показывать по очереди в одном окне прямоугольной формы. Обычно для управления процессом перебора диалоговых панелей в режиме CardLayout используются отдельные органы управления, расположенные в другой панели или даже в другом аплете на той же самой странице сервера WWW. Пример такого аплета мы приведем после того как рассмотрим использование класса Panel.
Класс CardLayout содержит два конструктора и несколько методов:
public class java.awt.CardLayout
extends java.lang.Object
implements java.awt.LayoutManager
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Режим без зазоров
public CardLayout();
// Режим с зазорами по вертикали и горизонтали
// между компонентами и окном контейнера
public CardLayout(int hgap, int vgap);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Добавление компоненты с указанием имени
public void addLayoutComponent(String name,
Component comp);
// Отображение первой страницы блокнота
public void first(Container target);
// Отображение последней страницы блокнота
public void last(Container target);
// Отображение следующей страницы блокнота
public void next(Container target);
// Отображение предыдущей страницы блокнота
public void previous(Container target);
// Выполнение размещения компонент
public void layoutContainer(Container target);
// Определение минимальных размеров окна,
// необходимых для размещения компонент
public Dimension minimumLayoutSize(Container target);
// Определение предпочтительных размеров окна,
// необходимых для размещения компонент
public Dimension preferredLayoutSize(Container target);
// Удаление заданной компоненты
public void removeLayoutComponent(Component comp);
// Отображение произвольной страницы блокнота
// по ее имени
public void show(Container target, String name);
// Получение текстовой строки названия режима размещения
public String toString();
}
Как пользоваться режимом размещения CardLayout?
Обычно в окне аплета создается две панели, одна из которых предназначена для отображения страниц блокнота в режиме размещения CardLayout, а вторая содержит органы управления перелистыванием страниц, например, кнопки.
Такие методы, как first, last, next и previous позволяют отображать, соответственно, первую, последнюю, следующую и предыдущую страницу блокнота. Если вызвать метод next при отображении последней страницы, в окне появится первая страница. Аналогично, при вызове метода previous для первой страницы блокнота вы увидите последнюю страницу.
А как отобразить произвольную страницу, не перебирая их по одной методами next и previous?
Для этого существует метод show. Учтите, что этот метод позволяет отображать только такие страницы, при добавлении которых методом add было указано имя, например:
pCardPanel.add("BackgroundColor", pBackgroundColor);
pCardPanel.add("ForegroundColor", pForegroundColor);
pCardPanel.add("Font", pFont);
Здесь в панель pCardPanel добавляются панели pBackgroundColor, pForegroundColor и pFont, имеющие имена, соответственно, "BackgroundColor", "ForegroundColor" и "Font".
Режим FlowLayout
В этом режиме мы добавляли компоненты во всех примерах аплетов, приведенных в предыдущей главе, так как по умолчанию для аплетов используется именно режим FlowLayout.
Ниже мы привели краткое описание класса FlowLayout:
public class java.awt.FlowLayout
extends java.lang.Object
implements java.awt.LayoutManager
{
// -----------------------------------------------------
// Поля
// -----------------------------------------------------
// Способы выравнивания
public final static int CENTER; // центрирование
public final static int LEFT; // по левой границе
public final static int RIGHT; // по правой границе
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Без указания выравнивания и зазора между компонентами
public FlowLayout();
// С указанием выравнивания
public FlowLayout(int align);
// С указанием выравнивания и зазора между компонентами
// по вертикали и горизонтали
public FlowLayout(int align, int hgap, int vgap);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Не используется
public void addLayoutComponent(String name,
Component comp);
// Предназначен для того чтобы компоненты могли
// установить для себя предпочтительный размер
public void layoutContainer(Container target);
// Определение минимального размера окна контейнера,
// необходимого для размещения всех компонент
public Dimension minimumLayoutSize(Container target);
// Определение предпочтительного размера окна контейнера,
// необходимого для размещения всех компонент
public Dimension preferredLayoutSize(Container target);
// Удаление компоненты из контейнера
public void removeLayoutComponent(Component comp);
// Получение строки названия метода компоновки
public String toString();
}
Обычно приложения не вызывают методы класса FlowLayout, устанавливая варианты компоновки при помощи конструкторов.
Первый конструктор класса FlowLayout, не имеющий параметров, устанавливает по умолчанию режим центрирования компонент и зазор между компонентами по вертикали и горизонтали, равный 5 пикселам. Именно этот режим и использовался во всех наших аплетах из предыдущей главы, так как именно он применяется по умолчанию объектами класса Panel, от которого наследуется класс Applet.
С помощью второго конструктора вы можете выбрать режим размещения с заданным выравниванием компонент в окне контейнера по горизонтали. В качестве параметров этому конструктору необходимо передавать значения FlowLayout.LEFT, FlowLayout.RIGHT, или FlowLayout.CENTER. Зазор между компонентами будет при этом равен по умолчанию 5 пикселам.
И, наконец, третий конструктор допускает раздельное указание режима выравнивания, а также зазоров между компонентами по вертикали и горизонтали в пикселах.
Режим GridBagLayout
Режим GridBagLayout намного сложнее только что описанного режима GridLayout. Он позволяет размещать компоненты разного размера в таблице, задавая при этом для отдельных компонент размеры отступов и количество занимаемых ячеек.
В нашей книге мы не будем рассматривать этот режим, так как сходные результаты могут быть достигнуты другими, менее сложными способами. Например, вы можете создать в контейнере несколько панелей, использовав внутри каждой свой метод размещения компонент.
Если вы создаете аплеты для размещения в документах HTML, никто не заставляет вас ограничиваться только одним аплетом для одного документа HTML - вы можете разместить там произвольное количество аплетов, организовав взаимодействие с одной стороны, между отдельными аплетами, а с другой - между аплетами и расширениями сервера WWW.
Тех, кого интересует режим GridBagLayout, мы адресуем к документации, которая входит в комплект Microsoft Visual J++, а также к книге Д. Родли “Создание Java-апплетов”, которая издана на русском языке.
В интегрированной системе разработки приложений Java Microsoft Visual J++ версии 1.1 имеется система автоматизированного проектирования пользовательского интерфейса, в результате работы которой создаются исходные тексты классов. Размещение органов управления при этом выполняется интерактивными средствами, аналогичными средствам разработки диалоговых панелей для приложений Microsoft Windows. В следующем томе “Библиотеки системного программиста”, посвященном Java, мы научим вас пользоваться этой системой.
Режим GridLayout
В режиме GridLayout компоненты размещаются в ячейках таблицы, параметры которой можно задать с помощью конструкторов класса GridLayout:
public class java.awt.GridLayout
extends java.lang.Object
implements java.awt.LayoutManager
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание таблицы с заданным
// количеством строк и столбцов
public GridLayout(int rows, int cols);
// Создание таблицы с заданным количеством строк и
// столбцов и с заданным зазором между компонентами
public GridLayout(int rows, int cols, int hgap, int vgap);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
public void addLayoutComponent(String name,
Component comp);
public void layoutContainer(Container target);
public Dimension minimumLayoutSize(Container target);
public Dimension preferredLayoutSize(Container target);
public void removeLayoutComponent(Component comp);
public String toString();
}
При размещении компонент внутри ячеек таблицы все они получают одинаковые размеры. Если один из параметров, задающих размерность таблицы, равен нулю, это означает, что соответствующий столбец или строка может содержать любое количество элементов.
Заметим, что оба параметра rows и cols не могут быть равны нулю одновременно.
Режимы системы Layout Manager
Прежде чем мы рассмотрим различные режимы компоновки системы Layout Manager, вспомним, как происходит наследование класса Applet (рис.6.1).
Рис. 6.1. Наследование класса Applet
Класс Applet наследуется от класса Panel, который, в свою очередь, наследуется от класса Container и Component. Класс Container пользуется интерфейсом LayoutManager, что позволяет выбирать для контейнеров один из нескольких режимов размещения компонент в окне контейнера.
Что же касается класса Panel, то для него по умолчанию выбирается режим размещения компонент с названием Flow Layout. Разумеется, вы можете выбрать другой режим размещения, указав его явным образом.
Ниже мы перечислили все возможные режимы системы Layout Manager:
Режим размещения компонент
Описание
FlowLayout
Компоненты заполняют окно контейнера “потоком” по мере их добавления методом add. Они размещаются слева направо и сверху вниз
GridLayout
Компоненты размещаются в виде таблицы по мере добавления слева направо и сверху вниз. Для этой таблицы можно указать количество столбцов и строк
GridBagLayout
Аналогично предыдущему, однако при добавлении компонент в таблицу можно указать координаты ячейки, в которую помещается компонента
BorderLayout
При размещении компоненты указывается одно из нескольких направлений: юг, север, запад, восток, центр. Направление определяется относительно центра окна контейнера
CardLayout
Размещение компонент друг над другом в одном окне. Этот режим позволяет организовать набор диалоговых панелей в виде блокнота
Каждому режиму соответсвует одноименный класс, методы и конструкторы которого позволяют выбирать различные компоновки.
Далее на примере конкретных приложений мы рассмотрим использование перечисленных выше режимов системы Layout Manager.
Рисование геометрических фигур
В этом разделе мы опишем методы класса Graphics, предназначенные для рисования элементарных геометрических фигур, таких как линии, прямоугольники, окружности и так далее.
Рисование в окне панели
Как вы знаете, для того чтобы что-нибудь нарисовать, необходимо вначале получить контекст отображения. Методу paint передается контекст отображения, связанный с окном аплета. Если в окне имеются панели, то для рисования внутри них необходимо получить контекст отображения окон панелей.
Проще всего это сделать с помощью метода getGraphics, вызвав его для объекта класса Panel:
Graphics gpDraw;
gpDraw = pDraw.getGraphics();
Здесь в переменную gpDraw мы записали ссылку на контекст отображения для панели pDraw.
Получив контекст отображения, можно приступить к рисованию. Вот, например, как можно нарисовать вокруг панели тонкую рамку:
Dimension dimAppWndDimension = pDraw.size();
gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
В этом фрагменте кода мы вначале определили размеры панели, вызвав для нее метод size, а затем при помощи метода drawRect, вызванного для контекста отображения gpDraw, нарисовали рамку.
Для установки шрифта и рисования текста в окне панели вы также должны указывать ссылку на контекст отображения вашей панели:
gpDraw.setFont(new Font(“Courier”, Font.PLAIN, 12));
gpDraw.drawString("Текст внутри окна панели", 10, 50);
Другой способ основан на создании собственного класса на базе класса Panel и переопределения в этом классе метода paint. Мы рассмотрим его позже в разделе “Переопределение класса Panel”.
Сборка мусора
Одна из интереснейших особенностей языка программирования Java и среды выполнения приложений Java заключается в наличии специального процесса сборки мусора, предназначенного для удаления ненужных объектов из памяти. Эта система избавляет программиста от необходимости внимательно следить за использованием памяти, освобождая ненужные более области явным образом.
Создавая объекты в Java, вы можете руководствоваться принципом “Создай и забудь”, так как система сборки мусора позаботится об удалении ваших объектов. Объект будет удален из памяти, как только на него не останется ни одной ссылки из других объектов.
Приоритет процесса сборки мусора очень низкий, поэтому “уборка” среды выполнения приложений Java не отнимает ресурсы у самих приложений.
Сдвиг начала системы координат
Метод translate сдвигает начало системы координат в контексте отображения таким образом, что оно перемещается в точку с координатами (x, y), заданными через параметры метода:
public abstract void translate(int x, int y);
Сегменты
Метод drawArc предназначен для рисования незаполненного сегмента (рис. 3.8). Прототип этого метода приведен ниже:
public abstract void drawArc(int x, int y,
int width, int height, int startAngle, int arcAngle);
Рис. 3.8. Рисование незаполненного сегмента
Параметры x, y, width и height задают координаты прямоугольника, в который вписан сегмент.
Параметры startAngle и arcAngle задаются в градусах. Они определяют, соответственно, начальный угол и угол разворота сегмента.
Для того чтобы нарисовать заполненный сегмент, вы можете воспользоваться методом fillArc:
public abstract void
fillArc(int x, int y, int width,
int height, int startAngle, int arcAngle);
События от клавиатуры
Аплет может обрабатывать события, создаваемые клавиатурой. Например, он может реагировать на функциональные клавиши или на клавиши ускоренного выбора функций.
Для того чтобы обработать события от клавиатуры, ваш аплет должен переопределить методы keyDown и keyUp:
public boolean keyDown(Event evt, int nKey)
{
. . .
}
public boolean keyUp(Event evt, int nKey)
{
. . .
}
В качестве первого параметра этим методам передается объект типа Event, о полях которого мы рассказывали в разделе “Как обрабатываются события” этой главы.
Наибольший интерес представляют поля объекта evt с именами key и modifiers. Через них передается, соответственно, код нажатой клавиши и код модификации. Возможные значения для этих полей вы найдете в только что указанном разделе.
Второй параметр методов keyDown и keyUp с именем nKey дублирует поле key объекта evt.
События от мыши
В этом разделе мы рассмотрим события, которые возникают в результате того, что пользователь выполняет в окне аплета операции с мышью. Это такие операции, как нажатие и отпускание клавиши мыши, перемещение курсора мыши в окне аплета с нажатой или отпущенной клавишей, перемещение курсора мыши в окно аплета и удаление этого курсора из окна аплета.
Все перечисленные ниже методы должны вернуть значение true, если обработка события выполнена успешно и дальнейшая обработка не требуется. Если же методы вернут значение fasle, событие будет обработано методом из базового класса, то есть для него будет выполнена обработка, принятая по умолчанию.
Программисты, создававшие приложения для операционной системы Microsoft Windows, могут найти здесь аналогию с вызовом функции DefWindowProc, которая выполняет обработку сообщений, принятую по умолчанию.
Создание диалоговых панелей
Диалоговые панели создаются на базе класса Dialog, краткое описание которого приведено ниже:
public class java.awt.Dialog
extends java.awt.Window
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание диалоговой панели без заголовка
public Dialog(Frame parent, boolean modal);
// Создание диалоговой панели с заголовком
public Dialog(Frame parent, String title, boolean modal);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createDialog
public void addNotify();
// Получение строки заголовка диалоговой панели
public String getTitle();
// Определение, является ли диалоговая панель модальной
public boolean isModal();
// Определение возможности изменения размеров окна
// диалоговой панели
public boolean isResizable();
// Получение строки параметров
protected String paramString();
// Включение или выключение возможности изменения
// размеров окна диалоговой панели
public void setResizable(boolean resizable);
// Установка заголовка диалоговой панели
public void setTitle(String title);
}
Для того чтобы создать свою диалоговую панель, вы должны определить новый класс, унаследовав его от класса Dialog, как это показано ниже:
class MessageBox extends Dialog
{
. . .
public MessageBox(String sMsg,
Frame parent, String sTitle, boolean modal)
{
super(parent, sTitle, modal);
. . .
resize(200, 100);
. . .
}
}
В этом классе нужно определить конструктор, который вызывает конструктор базового метода класса Dialog и определяет размеры окна диалоговой панели. Кроме того, в конструкторе вы должны создать все необходимые компоненты для размещения внутри диалоговой панели (кнопки, списки, текстовые поля, переключатели и так далее), а также выполнить размещение этих компонент, установив нужный режим размещения.
Для окон класса Dialog устанавливается режим размещения BorderLayout. Если нужен другой режим размещения, необходимо установить его явным образом методом setLayout.
Для отображения окна диалоговой панели необходимо вызвать метод show. Чтобы спрятать диалоговой окно, применяйте метод hide. Метод dispose удаляет окно диалоговой панели окончательно и освобождает все связанные с ним ресурсы.
Когда пользователь пытается уничтожить окно диалоговой панели при помощи органов управления, расположенных в заголовке такого окна, возникает событие Event.WINDOW_DESTROY. Вы должны обработать его, обеспечив удаление окна диалоговой панели вызовом метода dispose, если, конечно, это соответствует логике работы вашей панели.
Создание нового класса на базе класса Panel
Если ваш аплет создает много панелей, техника рисования в окнах этих панелей, использованная в только что рассмотренном приложении Notebook, может привести к усложнению исходного текста приложения. Так как рисование в окнах панелей выполняется в методе paint класса аплета, вам придется получать контекст отображения для каждой панели.
Намного проще создать несколько дочерних классов от класса Panel, переопределив в каждом из них метод paint. В этом случае для каждой панели вы можете создать свой метода paint, которому будет автоматически передаваться контекст отображения, связанный с окном соответствующей панели.
Эта техника использована в приложении Panel2, которое мы рассмотрим в следующем разделе.
Создание панелей
Панель создается очень просто. Прежде всего необходимо выбрать для окна аплета схему размещения компонент, соответствующую требуему расположению панелей. Например, для создания в окне аплета двух панелей, разделяющих его по горизонтали, следует выбрать режим GridLayout:
setLayout(new GridLayout(2, 1));
Панели будут размещаться в ячейках таблицы, состоящей из одного столбца и двух строк.
Далее нужно создать объекты класса Panel:
Panel pTopPanel;
pTopPanel = new Panel();
Panel pBottomPanel;
pBottomPanel = new Panel();
Ссылка на панель, которая будет располагаться сверху, записывается в переменную pTopPanel, а на ту, что будет располагаться снизу - в переменную pBottomPanel.
Создание переключателей с независимой фиксацией
Переключатели с независимой и зависимой фиксацией создаются на базе класса Checkbox:
public class java.awt.Checkbox
extends java.awt.Component
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание переключателя с независимой фиксацией
// без названия
public Checkbox();
// Создание переключателя с независимой фиксацией
// и названием
public Checkbox(String label);
// Создание переключателя с зависимой фиксацией
// и названием
public Checkbox(String label, CheckboxGroup group,
boolean state);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createCheckbox
public void addNotify();
// Получение группы, к которой относится
// данный переключатель с зависимой фиксацией
public CheckboxGroup getCheckboxGroup();
// Получение названия переключателя
public String getLabel();
// Определение текущего состояния переключателя
public boolean getState();
// Получение строки параметров
protected String paramString();
// Установка группы, к которой относится
// данный переключатель с зависимой фиксацией
public void setCheckboxGroup(CheckboxGroup g);
// Установка названия переключателя
public void setLabel(String label);
// Установка нового состояния переключателя
public void setState(boolean state);
}
Создать переключатель с независимой фиксацией не сложнее, чем создать кнопку:
Checkbox rdbox1;
. . .
public void init()
{
chbox1 = new Checkbox("Switch 1");
add(chbox1);
}
В этом фрагменте кода мы создаем переключатель chbox1 с названием Switch 1, а затем с помощью метода add добавляем его в контейнер, которым является окно аплета.
Для определения текущего состояния переключателя вы можете использовать метод getState. Если переключатель включен, этот метод возвращает значение true, а если выключен - значение false.
Создание переключателей с зависимой фиксацией
Для каждой группы переключателей с зависимой фиксацией вы должны создать объект класса CheckboxGroup:
public class java.awt.CheckboxGroup
extends java.lang.Object
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public CheckboxGroup();
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Получение ссылки на переключатель, который
// находится во включенном состоянии
public Checkbox getCurrent();
// Установка указанного переключателя в группе
// во включенное состояние
public void setCurrent(Checkbox box);
// Получение строки, которая представляет группу
public String toString();
}
Ссылка на этот объект указывается при создании отдельных переключателей с зависимой фиксацией, входящих в группу:
CheckboxGroup grModeGroup;
Checkbox rdbox1;
Checkbox rdbox2;
Checkbox rdbox3;
Checkbox rdbox4;
. . .
public void init()
{
grModeGroup = new CheckboxGroup();
rdbox1 = new Checkbox("Mode 1",grModeGroup, true);
rdbox2 = new Checkbox("Mode 2",grModeGroup, false);
rdbox3 = new Checkbox("Mode 3",grModeGroup, false);
rdbox4 = new Checkbox("Mode 4",grModeGroup, false);
add(rdbox1);
add(rdbox2);
add(rdbox3);
add(rdbox4);
}
Через первый параметр конструктору Checkbox в этом примере передается название переключателя, через второй - ссылка на группу, а через третий - состояние, в которое должен быть установлен переключатель. Из всех переключателей группы только один может находиться во включенном состоянии.
Списки класса Choice
На базе класса Choice вы можете создать списки типа Drop Down или, как их еще называют, “выпадающие” списки. Такой список выглядит как текстовое поле высотой в одну строку, справа от которого располагается кнопка (рис. 5.5).
Рис. 5.5. Список типа Drop Down, созданный на базе класса Choice
Если нажать на эту кнопку, список раскроется и вы сможете сделать выбор из его элементов (рис. 5.6).
Рис. 5.6. Раскрытый список, созданный на базе класса Choice
В списке класса Choice одновременно можно выбрать только один элемент.
Рассмотрим класс Choice. Определение этого класса выглядит несложно:
public class java.awt.Choice
extends java.awt.Component
{
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public Choice();
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Добавление элемента в список
public void addItem(String item);
// Вызов метода createChoice
public void addNotify();
// Определение количества элементов в списке
public int countItems();
// Получение строки списка по номеру соответствующего
// ему элемента списка
public String getItem(int index);
// Получение номера текущего выбранного элемента
public int getSelectedIndex();
// Получение строки, соответствующей текущему
// выбранному элементу списка
public String getSelectedItem();
// Получение строки параметров
protected String paramString();
// Выбор в списке элемента по заданному номеру
public void select(int pos);
// Выбор в списке элемента по заданной строке
public void select(String str);
}
Конструктор класса Choice не имеет параметров. Создание списка с его помощью не вызовет у вас никаких затруднений:
Choice chBackgroundColor;
chBackgroundColor = new Choice();
Для наполнения списка используйте метод addItem. В качестве параметра ему необходимо передать текстовую строку, которая будет связана с добавляемым элементом списка:
chBackgroundColor.addItem("Yellow");
Далее список можно добавить в окно аплета как компонент с помощью метода add:
add(chBackgroundColor);
Заметим, что список можно заполнять до или после добавления в окно аплета.
После наполнения списка по умолчанию выделяется элемент, который был добавлен в список первым. При помощи метода select вы можете выделить любой элемент списка по его номеру или строке, связанной с элементом.
Когд пользователь выбирает новую строку в списке, возникает событие. Обработчик этого события, реализованный, например, переопределением метода action, может получить номер выбранной строки при помощи метода getSelectedIndex. Пример обработки такого события вы найдете в разделе “Приложение ChoiceList”.
Если вас интересует не номер выбранного элемента, а строка, связанная с выбранным элементом, воспользуйтесь методом getSelectedItem.
И, наконец, с помощью метода getItem вы можете получить текст строки, связанной с элементом, по номеру элемента.
Списки класса List
На базе класса List вы можете сделать список другого типа, который допускает выбор не только одного, но и нескольких элементов. В отличие от списка, созданного на базе класса Choice, список класса List может занимать прямоугольную область, в которой помещаются сразу несколько элементов. Этот список всегда находится в раскрытом состоянии (рис. 5.8).
Рис. 5.8. Список класса List, все элементы которого помещаются в окне списка
Если размеры окна списка класса List недостаточны для того чтобы вместить в себя все элементы, в правой части окна списка автоматически создается полоса просмотра, с помощью которой можно пролистать весь список (рис. 5.9).
Рис. 5.9. Список класса List с полосой просмотра
Ссылки на методы класса
Так как в Java нет указателей, нет возможности ссылаться на методы с помощью оператора ->. Для ссылки на метод класса используется только оператор “точка”.
Оператор “::” также не определен в Java. Если вам необходимо вызвать метод из базового класса, следует использовать ключевое слово super.
Текстовое поле класса Label
На базе класса Label вы можете создать в окне аплета однострочное текстовое поле, которое не поддается редактированию. Основное назначение таких полей - подпись других компонент, таких, например, как группы переключателей или списки.
Ниже мы привели краткое описание класса Label:
public class java.awt.Label
extends java.awt.Component
{
// -----------------------------------------------------
// Поля
// -----------------------------------------------------
// Способ выравнивания текстового поля
public final static int CENTER; // центрирование
public final static int LEFT; // по левой границе
public final static int RIGHT; // по правой границе
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание текстового поля без текста
public Label();
// Создание текстового поля с заданным текстом
public Label(String label);
// Создание текстового поля с заданным текстом
// и заданным выравниванием
public Label(String label, int alignment);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createLabel
public void addNotify();
// Определение текущего выравнивания текстового поля
public int getAlignment();
// Получение текста из поля
public String getText();
// Получение строки параметров
protected String paramString();
// Установка выравнивания текстового поля
public void setAlignment(int alignment);
// Запись текста в поле
public void setText(String label);
}
Текстовое поле класса Label создается вызовом соответствующего конструктора. Например, ниже мы создали текстовое поле, указав строку, которую надо в него записать:
Label lbTextLabel;
lbTextLabel = new Label("Выберите выравнивание");
С помощью метода add вы можете добавить текстовое поле в окно аплета:
add(lbTextLabel);
Метод setAlignment позволяет при необходимости изменить выравнивание текста. Способ выравнивания необходимо указать через единственный параметр метода:
lbTextLabel.setAlignment(Label.LEFT);
При помощи метода setText вы сможете динамически изменять текст, расположенный в поле класса Label.
Текстовое поле класса TextField
Для редактирования одной строки текста вы можете создать текстовое поле на базе класса TextField, которое несложно в использовании. Класс TextField создан на базе другого класса с именем TextComponent, поэтому при работе с текстовым полем класса TextField вы можете использовать и методы класса TextComponent.
Приведем краткое описание класса TextField:
public class java.awt.TextField
extends java.awt.TextComponent
{
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание поля без текста
public TextField();
// Создание поля без текста с заданной шириной
public TextField(int cols);
// Создание поля и инициализация его текстом
public TextField(String text);
// Создание поля заданной ширины
// и инициализация его текстом
public TextField(String text, int cols);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Вызов метода createTextField
public void addNotify();
// Проверка, установлен ли для поля эхо-символ
public boolean echoCharIsSet();
// Определение размера поля
public int getColumns();
// Получение текущего эхо-символа
public char getEchoChar();
// Определение минимальных размеров области
// для отображения поля
public Dimension minimumSize();
// Определение минимальных размеров области
// для отображения поля заданной ширины
public Dimension minimumSize(int cols);
// Получение строки параметров
protected String paramString();
// Определение оптимальных размеров области
// для отображения поля
public Dimension preferredSize();
// Определение оптимальных размеров области
// для отображения поля заданной ширины
public Dimension preferredSize(int cols);
// Установка эхо-символа для отображения в поле
public void setEchoCharacter(char c);
}
При создании текстового поля вы можете выбрать один из четырех конструкторов, соответственно, для создания поля без текста и без указания размера, без текста заданного размера, для создания поля с текстом и для создания поля с текстом указанного размера.
Вот фрагмент кода, в котором создается поле с текстом, имеющее ширину, достаточную для размещения 35 символов:
TextField txt;
txt = new TextField("Введите строку текста", 35);
Созданное поле добавляется в окно аплета методом add.
Большинство самых полезнных методов, необходимых для работы с полем класса TextField, определено в классе TextComponent, краткое описание которого мы привели ниже:
public class java.awt.TextComponent
extends java.awt.Component
{
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Получение текста, выделенного пользователем
// в окне поля
public String getSelectedText();
// Получение позиции конца выделенной области
public int getSelectionEnd();
// Получение позиции начала выделенной области
public int getSelectionStart();
// Получение полного текста из поля
public String getText();
// Проверка, возможно ли редактирование
// текста в поле
public boolean isEditable();
// Получение строки параметров
protected String paramString();
// Удаление извещения
public void removeNotify();
// Выделение заданной области текста
public void select(int selStart, int selEnd);
// Выделение всего текста
public void selectAll();
// Включение или выключение возможности
// редактирования текста
public void setEditable(boolean t);
// Установка текста в поле
public void setText(String t);
}
С помощью метода getText вы можете получить весь текст, который имеется в поле. Метод getSelectedText позволяет получить только ту часть текста, которая предварительно была выделена пользователем. Приложение может выделить любой фрагмент текста или весь текст при помощи методов select и selectAll, соответственно.
Для записи текста в поле приложение может воспользоваться методом setText.
Возможно, для вас будет интересен метод setEditable, позволяющий переключать текстовое поля из режима, при котором редактирование заблокировано, в режим с разрешенным редактированием и обратно.
Указатели, которых нет
Самая большая и шокирующая новость для тех, кто раньше программировал на С, а теперь занялся изучением Java, это то, что в языке Java нет указателей. Традиционно считалась, что работать с указателями трудно, а их использование приводит к повялению трудно обнаруживаемых ошибок. Поэтому разработчики Java решили отказаться от использования указателей совсем.
Спешим успокоить - вы сможете успешно составлять приложения Java и без указателей, несмотря на то что вам, возможно, придется немного изменить стиль программирования.
Вы можете спросить: как же передавать функциям ссылки на объекты, если нет указателей?
Если вам нужно передать ссылку на переменную базового типа, такого, например, как int или long, то ничего не получится - мы уже говорили, что переменные базовых типов передаются по значению, а не по ссылке. Поэтому вы не сможете напрямую создать на языке Java эквивалент следующей программы, составленной на языке С:
// Некоторая переменная
int nSomeValue;
// Функция, изменяющая значение переменной,
// заданной своим адресом
void StoreValue(int *pVar, int nNewValue)
{
pVar->nNewValue;
}
. . .
StoreValue(&nSomeValue, 10);
Выход, однако, есть.
Язык Java позволяет использовать вместо указателей ссылки на объекты. Пользуясь этими ссылками, вы можете адресовать объекты по их имени, вызывая методы и изменяя значения данных объектов.
Что же касается данных базовых типов, если вам нужно передавать на них ссылки, то следует заменить базовые типы на соответствующие встроенные классы. Например, вместо типа int используйте класс Integer, вместо типа long - класс Long и так далее.
Инициализация таких объектов должна выполняться с помощью конструктора, как это показано ниже:
Integer nSomeValue;
nSomeValue = new Integer(10);
Первая строка создает неинициализированную ссылку с именем nSomeValue и типом Integer. При попытке использования такой ссылки возникнет исключение.
Вторая строка создает объект класса Integer, вызывая конструктор. Этот конструктор определяет начальное значение. После выполнения оператора присваивания ссылка nSomeValue будет ссылаться на реальный объект класса Integer и ее можно будет использовать.
Имя объекта nSomeValue типа Integer вы можете передавать функциям в качестве параметра, причем это будет ссылкой на объект.
Составляя программы на языке С, вы часто использовали указатели для адресации элементов массивов, созданных статически или динамически в оперативной памяти. Зная начальный адрес такого массива и тип хранящихся в нем элементов, вы могли адресоваться к отдельным элементам массива.
В языке Java реализован механизм массивов, исключающих необходимость использования указателей.
Упрощаем исходный текст аплета
Если вам показалось, что исходный текст аплета слишком сложный, вы можете его упростить, как это показано ниже:
//**********************************************************
// HelloAp.java: Applet
//
//**********************************************************
import java.applet.*;
import java.awt.*;
//==========================================================
// Основной класс для аплета HelloAp
//
//==========================================================
public class HelloAp extends Applet
{
// Обработчик процедуры рисования окна аплета HelloAp
//---------------------------------------------------
public void paint(Graphics g)
{
g.drawString(
"Created with Microsoft Visual J++ Version 1.0",
10, 20);
}
}
Мы выбросили определения всех методов, которые не выполняют никакой полезной работы, а также удалили методы getAppletInfo и init. Аплет будет работать также, как и раньше, потому что методы init, start, stop, destroy, getAppletInfo, удаленные нами, определены в базовом классе Applet.
Однако метод paint необходимо переопределить в любом случае, так как именно в нем выполняется рисование строки, то есть то, что делает наш аплет.
Почему же система Java Applet Wizard создает пустые определения методов? Просто для того, чтобы вы при необходимости наполнили их чем нибудь полезным. Если вам не нужны эти определения, вы можете их удалить.
Установка атрибутов контекста отображения
Изменяя атрибуты контекста отображения, приложение Java может установить цвет для рисования графических изображений, таких как линии и многоугольники, шрифт для рисования текста, режим рисования и маску. Возможен также сдвиг начала системы координат.
Установка маски для рисования
Задавая маску для рисования при помощи метода setXORMode, вы можете выполнить при рисовании замещение текущего цвета на цвет, указанный в параметре метода, и наоборот, цвета, указанного в параметре метода, на текущий.
Все остальные цвета изменяются непредсказуемым образом, однако эта операция обратима, если вы нарисуете ту же самую фигуру два раза на одном и том же месте.
Прототип метода setXORMode:
public abstract void setXORMode(Color c1);
Установка режима рисования
Метод setPaintMode устанавливает в контексте отображения режим рисования, при котором выполняется замещение изображения текущим цветом, установленном в контексте отображения.
Прототип метода setPaintMode приведен ниже:
public abstract void setPaintMode();
Вход курсора мыши в область окна аплета
Метод mouseEnter получает управление, когда курсор мыши в процессе перемещения по экрану попадает в область окна аплета:
public boolean mouseEnter(Event evt, int x, int y);
Вы можете использовать этот метод для активизации аплета, на который указывает курсор мыши.
Встроенные классы
В языке Java все классы происходят от класса Object, и, соответственно, наследуют методы этого класса. Некоторые библиотеки классов подключаются автоматически, и мы будем называть их встроенными. К таким относится, в частности, библиотека с названием java.lang. Другие библиотеки классов вы должны подключать в исходном тексте приложения Java явным образом с помощью оператора import, о котором мы еще расскажем.
Казалось бы, на сегодняшний день
Казалось бы, на сегодняшний день изобрели уже все языки программирования, какие только можно придумать. Но нет - появился еще один, с названием Java. Этот язык сумел завоевать весьма заметную популярность за последние несколько лет, так как он ориентирован на самую популярную компьютерную среду - сеть Internet и серверы WWW.
Язык Java произошел от языка программирования Oak (а не от С++, как думают многие). Oak был приспособлен для работы в Internet и затем переименован в Java. Изучая Java, вы будете приятно удивлены тем, что его синтаксис близок к синтаксису языка C++. Унаследовав самое лучшее от языка программирования C++, язык Java при этом избавился от некоторых недостатков С++, в результате чего на нем стало проще программировать. В этом языке нет, например, указателей, которые сложны в использовании и потенциально могут послужить причиной доступа программы к не принадлежащей ей области памяти. Нет множественного наследования и шаблонов, хотя функциональные возможности языка Java от этого не пострадали. Если вы умеете программировать на C++, для вас не составит особого труда изучить язык Java.
Огромное преимущество Java заключается в том, что на этом языке можно создавать приложения, способные работать на различных платформах. К сети Internet подключены компьютеры самых разных типов - совместимые с IBM PC, компьютеры фирмы Apple, рабочие станции Sun и так далее. Даже в рамках компьютеров, созданных на базе процессоров Intel, существует несколько платформ, например, Microsoft Windows версии 3.1, Microsoft Windows 95,. Microsoft Windows NT, IBM OS/2, Solaris, различные разновидности операционной системы UNIX с графической оболочкой X-Windows. Между тем, создавая сервер WWW в сети Internet, вы бы наверняка хотели, чтобы им могло пользоваться как можно большее число людей. В этом случае вас выручат мультиплатформные приложения Java, не зависящие от конкретного типа процессора и операционной системы.
Программы, составленные на языке программирования Java, можно разделить по своему назначению на две большие группы.
К первой группе относятся приложения Java, предназначенные для автономной работы под управлением специальной интерпретирующей машины Java. Реализации этой машины созданы для всех основных компьютерных платформ.
Вторая группа - это так называемые аплеты (applets). Аплеты представляют собой разновидность приложений Java, которые интерпретируются виртуальной машиной Java, встроенной в навигаторы WWW, такие как Microsoft Internet Explorer или Netscape Navigator.
Приложения, относящиеся к первой группе (в нашей книге мы будем называть их просто приложениями Java), это обычные автономные программы. Так как они не содержат машинного кода и работают под управлением специального интерпретатора, их производительность заметно ниже, чем у обычных программ, составленных, например, на языке программирования C++. Однако не следует забывать, что программы Java без перетрансляции способны работать на любой платформе, что само по себе имеет большое значение в плане разработок для Internet.
Аплеты Java встраиваются в документы HTML, хранящиеся на сервере WWW. С помощью аплетов вы можете сделать страницы сервера WWW динамичными и интерактивными. Аплеты позволяют выполнять сложную локальную обработку данных, полученных от сервера WWW и введенных пользователем с клавиатуры. Из соображений безопасности аплеты (в отличие от обычных приложений Java) не имеют никакого доступа к файловой системе локального компьютера. Все данные для обработки они могут получить только от сервера WWW. Более сложную обработку данных можно выполнять, организовав взаимодействие между аплетами и расширениями сервера WWW - приложениями CGI и ISAPI.
Для повышения производительности приложений Java в навигаторе Microsoft Internet Explorer использована технология с названием Just-in-Time Compilation, или JIT. При первой загрузке аплета его код транслируется в обычную исполнимую программу, которая сохраняется на диске и запускается. В результате общая скорость выполнения аплета Java увеличивается в несколько раз.
Язык Java является объектно-ориентированным и поставляется с достаточно объемной библиотекой классов. Так же как и библиотеки классов систем разработки приложений на языке С++, такие как Microsoft Foundation Classes (MFC), библиотеки классов Java значительно упрощают разработку приложений, представляя в распоряжение программиста мощные средства решения распространенных задач. Поэтому программист может больше внимания уделить решению прикладных задач, а не таких, как, например, организация динамических массивов, взаимодействие с операционной системой или реализация элементов пользовательского интерфейса.
Первоначально средства разработки приложений и аплетов Java были созданы фирмой Sun и до сих пор эти средства пользуются популярностью. В сети Internet по адресу http://www.sun.com есть сервер фирмы Sun, с которого можно бесплатно получить набор Java Development Kit (JDK). В JDK входят пакетные программы для компиляции исходных текстов приложений Java, виртуальная машина, программа автоматизированного создания документации по классам, справочник по классам Java и другие необходимые средства.
Для тех, кто привык пользоваться средствами разработки корпорации Microsoft, мы можем порекомендовать SDK-Java - пакетное средство разработки приложений и аплетов Java, расположенное на сервере http://microsoft.com. В составе этого средства есть также комплект документации по классам Java в виде набора документов HTML. Вы можете переписать себе SDK-Java бесплатно с указанного сревера Microsoft.
Если вы привыкли к интегрированным средствам разработки приложений, таким как Microsoft Visual C++, инструментарий JDK и SDK-Java могут показаться вам неудобными. В этом случае вы можете воспользоваться такими системами разработки приложений Java, как, например, Symantec Cafe или Microsoft Visual J++.
Мы рассмотрим недорогое, но удобное и мощное интегрированное средство разработки приложений Microsoft Visual J++. Оценочную версию этого средства можно бесплатно получить с сервера WWW корпорации Microsoft. Заметим, что эта оценочная версия не содержит внутри себя “бомбы”, уничтожающей программу через заданное время, и вы сможете оценивать ее достаточно долго. Стоимость коммерческой версии Microsoft Visual J++ невелика, особенно с учетом того, что в коробке вы найдете прекрасную книгу Стефана Дэвиса “Learn Java Now” (на английском языке) издательства Microsoft Press. Если вы работаете с Microsoft Visual C++, то после установки Microsoft Visual J++ вы получите единую среду для разработки на языках программирования C++ и Java, что очень удобно.
На прилавках книжных магазинов вы можете найти несколько переводных книг, посвященных программированию на языке Java. Практически все они ориентированы на инструментарий JDK, созданный фирмой Sun, и содержат более или менее подробное описание классов Java. В нашей книге мы научим вас работать с Microsoft Visual J++ и приведем все сведения, необходимые для разработки как автономных приложений, так и аплетов Java.
При изложении материала мы будем предполагать, что вы знакомы с языком программирования C++. Наша книга не является учебником по языку Java. Мы будем рассказывать о том, как использовать язык Java и систему разработки Microsoft Visual J++ для создания автономных приложений и аплетов Java, как организовать взаимодействие аплетов с сервером WWW (во второй части этой книги). При этом мы будем считать, что с основами языка Java вы знакомы.
Что вам потребуется для работы с книгой?
Очень хорошо, если вы подключены к сети Internet или имеете возможность хотя бы эпизодической работы в этой сети. В этом случае вам будет доступна бесплатная оценочная версия Microsoft Visual J++, другие бесплатные средства разработки приложений и аплетов Java, грандиозные запасы документации и примеров программ.
Для проверки работы аплетов вам следует установить навигатор Internet, способный запускать аплеты Java. Это Microsoft Internet Explorer версии 3.01 и Netscape Navigator версии 3.0. Заметим, что для запуска аплетов вам не нужно обязательно подключаться к Internet - вы можете встраивать аплеты в документы HTML, расположенные на локальном диске вашего компьютера и просматривать эти документы навигатором просто как локальные файлы.
Автономные приложения Java работают под управлением специального интерпретатора (виртуальной машины Java), поэтому для их отладки вам также не потребуется сеть Internet.
Если вы собираетесь проверять работу приложений и аплетов Java, взаимодействующих с сервером WWW, вы можете воспользоваться собственным сервером в Internet или в корпоративной сети Intranet (если они у вас есть). Примеры таких аплетов мы приведем в следующей книге “Библиотеки системного программиста”, посвященной Java. Можно также установить сервер WWW, входящий в комплект операционной системы Microsoft Windows NT Workstation версии 4.0, или Personal Web Service для операционной системы Microsoft Windows 95. Последний доступен для бесплатной загрузки с сервера http://microsoft.com.
Для того чтобы успешно работать с аплетами Java, вы должны иметь некоторое представление о серверах WWW и языке гипертекстовой разметки документов HTML. Поэтому перед чтением этой книги мы рекомендуем вам ознакомится с 29 томом “Библиотеки системного программиста”, который называется “Сервер Web своими руками”. В этой книге мы рассказали о том, как сделать собственный сервер WWW в сети Internet и Intranet, а также привели необходимую информацию о расширениях сервера WWW, реализованных как приложения CGI и ISAPI.
Базовые знания об Internet, а также описание конкретных способов подключения к ней в российских условиях вы найдете в 23 томе “Библиотеки системного программиста”, который называется “Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets”. Мы рекомендуем эту книгу для тех, кто только планирует подключиться к Internet, но еще не вполне осознает, как это можно сделать и для чего это нужно.
Что еще почитать?
Количество книг, посвященных Java, растет катастрофически, особенно за рубежом. Среди удачных еще раз назовем книгу Стефана Дэвиса с названием “Learn Java Now”, которая может служить учебником по языку Java для тех, кто никогда не программировал на С и С++.
Среди переводных книг, которые можно встретить в продаже, отметим книгу Джона Родли “Создание JAVA-апплетов”. Эта книга рассчитана на серьезных программистов, хорошо знающих язык программирования Java. Однако для тех, кто только начинает изучать язык Java, она может оказаться слишком сложной.
Другая книга, заслуживающая внимание, это книга Криса Джамса с названием “Java”. После небольшого введения, рассчитанного на начинающих, в этой книге приводится описание более чем дюжины достаточно интересных аплетов с исходными текстами и комментариями.
В качестве справочника по языку Java и библиотекам классов вы можете использовать книгу И. Баженовой “Язык программирования Java”, которая вышла в издательстве АО “Диалог -МИФИ”.
Ну и, конечно, вам следует ознакомиться с различными руководствами по языку Java, хранящимися в сети Internet. В качестве отправной точки для поиска вы можете выбрать сервер основного разработчика этого языка - фирмы Sun. Адрес ее сервера мы уже приводили: http://www.sun.com. Помимо документации и примеров программ на Java, здесь вы найдете ссылки на другие ресурсы, посвященные этому языку программирования. Попробуйте также воспользоваться поисковыми серверами, такими как Jahoo! и Alta Vista, указав в качестве ключевого слово “Java”.
Выбор цвета
Изменение цвета, выбранного в контекст отображения, выполняется достаточно часто. В классе Graphics для изменения цвета определен метод setColor, прототип которого представлен ниже:
public abstract void setColor(Color c);
В качестве параметра методу setColor передается ссылка на объект класса Color, с помощью которого можно выбрать тот или иной цвет.
Как задается цвет?
Для этого можно использовать несколько способов.
Прежде всего, вам доступны статические объекты, определяющие фиксированный набор основных цветов:
Объект
Цвет
public final static Color black;
черный
public final static Color blue;
голубой
public final static Color cyan;
циан
public final static Color darkGray;
темно-серый
public final static Color gray;
серый
public final static Color green;
зеленый
public final static Color lightGray;
светло-серый
public final static Color magenta;
малиновый
public final static Color orange;
оранжевый
public final static Color pink;
розовый
public final static Color red;
красный
public final static Color white;
белый
public final static Color yellow;
желтый
Этим набором цветов пользоваться очень просто:
public void paint(Graphics g)
{
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
g.drawString("Привет из аплета!", 10, 20);
. . .
}
Здесь мы привели фрагмент исходного текста метода paint, в котором в контексте отображения устанавливается желтый цвет. После этого метод drawString выведет текстовую строку "Привет из аплета!" желтым цветом.
Если необходима более точная установка цвета, вы можете воспользоваться одним из трех конструкторов объекта Color:
public Color(float r, float g, float b);
public Color(int r, int g, int b);
public Color(int rgb);
Первые два конструктора позволяют задавать цвет в виде совокупности значений трех основных цветовых компонент - красной, желтой и голубой (соотвестсвенно, параметры r, g и b). Для первого конструктора диапазон возможных значений компонент цвета находится в диапазоне от 0.0 до 1.0, а для второго - в диапазоне от 0 до 255.
Третий конструктор также позволяет задавать отдельные компоненты цвета, однако они должны быть скомбинированы в одной переменной типа int. Голубая компонента занимает биты от 0 до 7, зеленая - от 8 до 15, красная - от 16 до 23.
Ниже мы привели пример выбора цвета с помощью конструктора, передав ему три целочисленных значения цветовых компонент:
g.setColor(new Color(0, 128, 128));
В классе Color определено еще несколько методов, которые могут оказаться вам полезными:
Метод
Описание
public Color brighter();
Установка более светлого варианта того же цвета
public Color darker();
Установка более темного варианта того же цвета
public boolean equals(
Object obj);
Проверка равенства цветов текущего объекта и объекта, заданного параметром
public int getBlue();
Определение голубой компоненты цвета (в диапазоне от 0 до 255)
public int getRed();
Определение красной компоненты цвета (в диапазоне от 0 до 255)
public int getGreen();
Определение зеленой компоненты цвета (в диапазоне от 0 до 255)
getHSBColor(float h, float s,
float b);
Определение компонент оттенка, насыщенности и яркости (схема HSB)
public int getRGB();
Определение компонент RGB для цвета, выбранного в контекст отображения
public static int
HSBtoRGB(float hue,
float saturation,
float brightness);
Преобразование цветового представления из схемы HSB в схему RGB
public static float[]
RGBtoHSB(int r, int g,
int b, float hsbvals[]);
Преобразование, обратное выполняемому предыдущей функцией
public String toString();
Получение текстовой строки названия цвета
Второй способ установки цвето фона и изображения заключается в вызове методов setBackground и setForeground, например:
setBackground(Color.yellow);
setForeground(Color.black);
Здесь мы устанавливаем для окна аплета желтый цвет фона и черный цвет изображения.
Выбор шрифта
С помощью метода setFont из класса Graphics вы можете выбрать в контекст отображения шрифт, который будет использоваться методами drawString, drawBytes и drawChars для рисования текста. Вот прототип метода setFont:
public abstract void setFont(Font font);
В качестве параметра методу setFont следует передать объект класса Font:
public class java.awt.Font
extends java.lang.Object
{
// -----------------------------------------------------
// Поля класса
// -----------------------------------------------------
protected String name;
protected int size;
protected int style;
// Битовые маски стиля шрифта
public final static int BOLD;
public final static int ITALIC;
public final static int PLAIN
// -----------------------------------------------------
// Конструктор
// -----------------------------------------------------
public Font(String name, int style, int size);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Сравнение шрифтов
public boolean equals(Object obj);
// Определение названия семейства шрифтов
public String getFamily();
// Получение шрифта по его характеристикам
public static Font getFont(String nm);
public static Font getFont(String nm, Font font);
// Определение названия шрифта
public String getName();
// Определение размера шрифта
public int getSize();
// Определение стиля шрифта
public int getStyle();
// Получение хэш-кода шрифта
public int hashCode();
// Определение жирности шрифта
public boolean isBold();
// Проверка, является ли шрифт наклонным
public boolean isItalic();
// Проверка, есть ли шрифтовое выделение
public boolean isPlain();
// Плучение текстовой строки для объекта
public String toString();
}
Создавая шрифт конструктором Font, вы должны указать имя, стиль и размер шрифта.
В качестве имени можно указать, например, строки Arial или Courier. Учтите, что в системе удаленного пользователя, загрузившего ваш аплет, может не найтись шрифта с указанным вами именем. В этом случае навигатор заменит его на наиболее подходящий (с его точки зрения).
Стиль шрифта задается масками BOLD, ITALIC и PLAIN, которые можно комбинировать при помощи логической операции “ИЛИ”:
Маска
Описание
BOLD
Утолщенный шрифт
ITALIC
Наклонный шрифт
PLAIN
Шрифтовое выделение не используется
Что же касается размера шрифта, то он указывается в пикселах.
Выход курсора мыши из области окна аплета
Метод mouseExit вызывается при покидании куросром окна аплета:
public boolean mouseExit(Event evt, int x, int y);
Если пользователь убрал курсор из окна аплета, активизированного методом mouseEnter, то метод mouseExit может переключить аплет в пассивное состояние.
Выполнение операции Drag and Drop
Операция Drag and Drop выполняется следующим образом: пользователь нажимает клавишу мыши и, не отпуская ее, начинает перемещать курсор мыши. При этом происходит вызов метода mouseDrag:
public boolean mouseDrag(Event evt, int x, int y);
Через переменные x и y передаются текущие координаты курсора мыши. Метод mouseDrag вызывается даже в том случае, если в процессе перемещения курсор вышел за пределы окна аплета.
Взгляд на исходный текст приложения Hello
Давайте теперь взглянем еще раз на исходный текст приложения Hello и посмотрим, что там к чему. Так как этот текст невелик, для удобства приведем его снова:
public class hello
{
public static void main(String[] args)
{
System.out.println("Hello, Java!\n");
}
}
В приложении определен один класс public с именем hello. Исходный файл приложения Java может содержать только один класс public, причем имя файла должно в точности соответствовать имени такого класса. В данном случае исходный файл называется hello.java. Если бы вы назвали файл Hello.java, компилятор выдал бы сообщение об ошибке.
В классе hello мы определили один статический метод с именем main. Статическая функция с этим именем является точкой входа приложения Java, если она определена с классе public с именем, таким же как и имя файла.
В качестве параметра функции main передается ссылка на массив строк класса String. Через эти строки вы можете передавать приложению Java параметры запуска.
Как наше приложение выводит текстовую строку на консоль?
В классе System определена переменная класса PrintStream с именем out. В классе PrintStream определен метод println, при помощи которой наше приложение выводит сообщение “Hello, Java!” на консоль.
Но где же объект, для которого вызывается метод println? В классе System поле PrintStream определено как статическое, поэтому методы этого класса можно вызывать, не создавая объектов класса, чем мы и воспользовались.
Как видите, текст пристейшего приложения Java по своей сложности не намного превосходит исходный текст программы аналогичного назначения, составленной на языке программирования С.
Задание области ограничения
Если для окна аплета задать область ограничения, то рисование будет возможно только в пределах этой области. Область ограничения задается методом clipRect, прототип которого мы привели ниже:
public abstract void
clipRect(int x, int y, int width, int height);
Параметры x, y, width и height задают координаты прямоугольной области ограничения.
Замещающие классы
Очень часто в наших приложениях вместо базовых типов переменных мы будем использовать объекты встроенных классов, которые называются замещающими классами (wrapper classes). Ниже мы перечислили названия этих классов и названия базовых типов данных, которые они замещают:
Базовый тип данных
Замещающий класс
boolean
Boolean
char
Character
int
Integer
long
Long
float
Float
double
Double
Заметим, что для преобразования базовых типов данных в объекты замещающего класса и обратно вы не можете применять оператор присваивания. Вместо этого необходимо использовать соответствующие методы замещающих классов, которые мы будем рассматривать по ходу дела. Полную информацию о классах и методах вы сможете найти в справочной системе, которая устанавливается вместе с Microsoft Visual J++, поэтому в нашей книге вы не найдете справочника по классам Java.
Microsoft Visual J++. Создание приложений и аплетов на языке Java
Мультизадачность
Наверное, сегодня уже нет необходимости объяснять, что такое мультизадачность. Все современные операционные системы, такие как Microsoft Windows 95, Microsoft Windows NT, IBM OS/2 или UNIX способны работать в мультизадачном режиме, повышая общую производительность системы за счет эффективного распараллеливания выполняемых задач. Пока одна задача находится в состоянии ожидания, например, завершения операции обмена данными с медленным периферийным устройством, другая может продолжать выполнять свою работу.
Пользователи уже давно привыкли запускать параллельно несколько приложений для того чтобы делать несколько дел сразу. Пока одно из них занимается, например, печатью документа на принтере или приемом электронной почты из сети Internet, другое может пересчитывать электронную таблицу или выполнять другую полезную работу. При этом сами по себе запускаемые приложения могут работать в рамках одной задачи - операционная система сама заботится о распределении времени между всеми запущенными приложениями.
Создавая приложения для операционной системы Microsoft Windows на языках программирования С или С++, вы могли решать многие задачи, такие как анимация или работа в сети, и без использования мультизадачности. Например, для анимации можно было обрабатывать сообщения соответствующим образом настроенного таймера.
Приложениям Java такая методика недоступна, так как в этой среде не предусмотрено способов периодического вызова каких-либо процедур. Поэтому для решения многих задач вам просто не обойтись без мультизадачности.
Работа с файлами
Библиотека классов языка программирования Java содержит многочисленные средства, предназначенные для работы с файлами. И хотя аплеты не имеют доступа к локальным файлам, расположенным на компьютере пользователя, они могут обращаться к файлам, которые находятся в каталоге сервера Web. Автономные приложения Java могут работать как с локальными, так и с удаленными файлами (через сеть Internet или Intranet).
В любом случае, будете ли вы создавать автономные приложения Java или аплеты, взаимодействующие с сервером Web через сеть, вы должны познакомиться с классами, предназначенными для организации ввода и вывода.
Создание сетевых приложений
Когда в 30 томе “Библиотеки системного программиста” мы начинали разговор про язык программирования Java, то отмечали, что он специально ориентирован на глобальные сети, такие как Internet. В этой главе мы начнем знакомство с конкретными классами Java, разработанными для сетевого программирования. На примере наших приложений вы сможете убедиться, что классы Java действительно очень удобны для создания сетевых приложений.
В этой главе мы рассмотрим два аспекта сетевого программирования. Первый из них касается доступа из приложений Java к файлам, расположенным на сервере Web, второй - создания серверных и клиентских приложений с использованием сокетов.
Напомним, что из соображений безопасности алпетам полностью запрещен доступ к локальным файлам рабочей станции, подключенной к сети. Тем не менее, аплет может работать с файлами, расположенными на серверах Web. При этом можно использовать входные и выходные потоки, описанные нами в предыдущей главе.
Для чего аплетам обращаться к файлам сервера Web?
Таким аплетам можно найти множество применений.
Представьте себе, например, что вам нужно отображать у пользователя диаграмму, исходные данные для построения которой находятся на сервере Web. Эту задачу можно решить, грубо говоря, двумя способами.
Первый заключается в том, что вы создаете расширение сервера Web в виде приложения CGI или ISAPI, которое на основании исходных данных динамически формирует графическое изображение диаграммы в виде файла GIF и посылает его пользователю. Что касается расширения сервера Web, то вы сможете его создать, пользуясь 29 томом “Библиотеки системного программиста”, который называется “Сервер Web своими руками”.
Однако на пути решения задачи с помощью расширения сервера Web вас поджидают две неприятности. Во-первых, создать из программы красивый цветной графический файл в стандарте GIF не так-то просто - вы должны разобраться с форматом этого файла и создать все необходимые заголовки. Во-вторых, графический файл занимает много места и передается по каналам Internet достаточно медленно - средняя скорость передачи данных в Internet составляет 1 Кбайт в секунду.
В то же время файл с исходными данными может быть очень компактным. Возникает вопрос - нельзя ли передавать через Internet только исходные данные, а построение графической диаграммы выполнять на рабочей станции пользователя?
В этом заключается второй способ, который предполагает применение аплетов. Ниже мы приведем исходные тексты соотвестсвующего аплета в разделе “Приложение ShowChart”. Это приложение получает через сеть файл исходных данных, а затем на основании содержимого этого файла рисует в своем окне цветную круговую диаграмму. Объем передаваемых данных при этом по сравнению с использованием расширения сервера Web сокращается десятки раз.
Помимо работы с файлами, расположенными на сервере Web, в этой главе мы расскажем о создании каналов между приложениями Java, работающими на различных компьютерах в сети, с использованием сокетов.
Сокеты позволяют организовать тесное взаимодействие аплетов и полноценных приложений Java, при котором аплеты могут предавать друг другу данные через сеть Internet. Это открывает широкие возможности для обработки информации по схеме клиент-сервер, причем в роли серверов здесь может выступать любой компьютер, подключенный к сети, а не только сервер Web. Каждая рабочая станция может выступать одновременно и в роли сервера, и в роли клиента.
Растровые изображения и анимация
Одно из наиболее распространенный применений аплетов связано с рисованием простых или анимированных растровых изображений. На серверах Web изображения обычно хранятся в форматах GIF или JPEG. Оба эти формата обеспечивают сжатие изображения, что весьма актуально из-за невысокой скорости передачи данных в сети Internet.
Рисование растровых изображений в приложениях для операционной системы Microsoft Windows, составленных на языке программирования С - не простая задача. В классическом программном интерфейсе этой операционной системы отсутствуют функции, с помощью которых можно было бы непосредственно рисовать содержимое файлов с растровыми изображениями.
Программист был вынужден работать с заголовками таких файлов, выделять таблицу цветов и биты изображений, создавать и реализовывать палитру, заниматься восстановлением сжатых данных и так далее. Мы описали этот непростой процесс для файлов формата BMP в 14 томе “Библиотеки системного программиста”, который называется “Графический интерфейс GDI в Microsoft Windows”.
Разработчик приложений Java находится в намного лучшем положении, так как библиотеки классов Java содержат простые в использовании и мощные средства, предназначенные для работы с растровыми изображениями. В этой главе мы научим вас рисовать в окне аплета содержимое файлов GIF и JPEG, выполняя при необходимости масштабирование, а также создавать на базе таких файлов анимационные изображения, показывая по очереди отдельные кадры небольшого видеофильма.
Звук в аплетах Java
Нельзя сказать, что звуковые возможности аплетов Java чрезмерно велики. Скорее наоборот, они минимальны. Тем не менее, аплеты могут проигрывать звуковые клипы, записанные в файлах формата AU, который пришел из мира компьютеров фирмы Sun.
Сказанное, однако, не означает, что если у вас нет рабочей станции Sun, то вы не сможете озвучить свои аплеты. Во-первых, в сети Internet можно найти много готовых звуковых файлов AU, а во-вторых, там же есть программы для преобразования форматов звуковых файлов. Одну из таких условно-бесплатных программ, которая называется GoldWave, вы можете загрузить с сервера ftp.winsite.com.
Взаимодействие между аплетами
До сих пор наши аплеты жили в документах HTML сами по себе. В этой главе мы расскажем о том, как организовать взаимодействие между аплетами, расположенными в одном и том же документе HTML.
Как аплеты могут взаимодействовать друг с другом?
С помощью интерфейса AppletContext, о котором вы скоро узнаете, аплеты могут получать списки всех аплетов для текущего документа HTML. Эти списки состоят из ссылок на соответствующие объекты класса Applet, пользуясь которыми можно получить доступ к полям и методам, определенным в этих аплетах как public, могут получать строку описания аплета, описание параметров и значения, передаваемые аплетам через параметры в документе HTML.
Таким образом, аплеты могут взаимодействовать друг с другом достаточно тесным образом.
Комбинированные приложения Java
Наши предыдущие приложения были либо аплетами, либо автономными приложениями с консольным окном. Основной класс аплетов был унаследован от класса Applet, а в классе консольных приложений был определен метод main.
В этой главе мы расскажем о том, как создавать комбинированные приложения, двоичный файл .class которых способен работать и как аплет, встроенный в документ HTML, и как автономное приложение. Система автоматизированного проектирования приложений Java Applet Wizard, встроенная в Microsoft Visual J++, позволяет автоматически создавать исходные тексты шаблонов таких комбинированных приложений.
Адрес IP и класс InetAddress
Прежде чем начинать создание сетевых приложений для Internet, вы должны разобраться с адресацией компьютеров в сети с протоколом TCP/IP, на базе которого построена сеть Internet. Подробную информацию об адресации вы можете получить из только что упомянутого 29 тома “Библиотеки системного программиста”. Здесь мы приведем только самые необходимые сведения.
Все компьютеры, подключенные к сети TCP/IP, называются узлами (в оригинальной терминологии узел - это host). Каждый узел имеет в сети свой адрес IP, состоящий из четырех десятичных цифр в диапазоне от 0 до 255, разделенных символом “точка “, например:
193.120.54.200
Фактически адрес IP является 32-разрядным двоичным числом. Упомянутые числа представляют собой отдельные байты адеса IP.
Так как работать с цифрами удобно лишь компьютеру, была придумана система доменных имен. При использовании этой системы адресам IP ставится в соответсвие так называемый доменный адрес, такой как www.microsoft.com.
В сети Internet имеется распределенная по всему миру база доменных имен, в которой установлено соответствие между доменными именами и адресами IP в виде четырех чисел.
Для работы с адресами IP в библиотеке классов Java имеется класс InetAddress, определение наиболее интересных методов которого приведено ниже:
public static InetAddress getLocalHost();
public static InetAddress getByName(String host);
public static InetAddress[] getAllByName(String host);
public byte[] getAddress();
public String toString();
public String getHostName();
public boolean equals(Object obj);
Рассмотрим применение этих методов.
Прежде всего вы должны создать объект класса InetAddress. Эта процедура выполняется не с помощью оператора new, а с применением статических методов getLocalHost, getByName и getAllByName.
Базовые классы для работы с файлами и потоками
Количество классов, созданных для работы с файлами, достаточно велико, чтобы привести начинающего программиста в растерянность. Прежде чем мы займемся конкретными классами и приведем примеры приложений, работающих с потоками и файлами, рассмотрим иерархию классов, предназначенных для орагнизации ввода и вывода.
Все основные классы, интересующие нас в этой главе, произошли от класса Object (рис. 2.1).
Рис. 2.1. Основные классы для работы с файлами и потоками
Блокировка на заданный период времени
С помощью метода sleep можно заблокировать задачу на заданный период времени. Мы уже пользовались этим методом в предыдущих приложениях, вызывая его в цикле метода run:
try
{
Thread.sleep(500);
}
catch (InterruptedException ee)
{
. . .
}
В данном примере работа задачи Thread приостанавливается на 500 миллисекунд. Заметим, что во время ожидания приостановленная задача не отнимает ресурсы процессора.
Так как метод sleep может создавать исключение InterruptedException, необходимо предусмотреть его обработку. Для этого мы использовали операторы try и catch.
Блокировка задачи
Синхронизированная задача, определенная как метод типа synchronized, может переходить в заблокированное состояние автоматически при попытке обращения к ресурсу, занятому другим синхронизированным методом, либо при выполнении некоторых операций ввода или вывода. Однако в ряде случаев полезно иметь более тонкие средства синхронизации, допускающие явное использование по запросу приложения.
Добавление буферизации
А что, если нам нужен не простой выходной поток, а буферизованный?
Здесь нам может помочь класс BufferedOutputStream. Вот два конструктора, предусмотренных в этом классе:
public BufferedOutputStream(OutputStream out);
public BufferedOutputStream(OutputStream out, int size);
Первый из них создает буферизованный выходной поток на базе потока класса OutputStream, а второй делает то же самое, но дополнительно позволяет указать размер буфера в байтах.
Если вам нужно создать выходной буферизованный поток для записи форматированных данных, создание потока выполняется в три приема:создается поток, связанный с файлом, как объект класса FileOutputStream;
ссылка на этот поток передается конструктору класса BufferedOutputStream, в результате чего создается буферизованный поток, связанный с файлом;
ссылка на буферизованный поток, созданный на предыдущем шаге, передается конструктору класса DataOutputStream, который и создает нужный поток
Вот фрагмент исходного текста программы, который создает выходной буферизованный поток для записи форматированных данных в файл с именем output.txt:
DataOutputStream OutStream;
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
Аналогичным образом создается входной буферизованный поток для чтения форматированных данных из того же файла:
DataInputStream InStream;
InStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("output.txt")));
Добавление изображений в объект класса MediaTracker
Далее метод init должен создать все необходимые объекты класса Image и добавить их в объект MediaTracker методом addImage. Ниже мы показали фрагмент кода, в котором выполняется добавление трех изображений:
Image img1;
Image img2;
Image img3;
img1 = getImage(getCodeBase(), "pic1.gif");
img2 = getImage(getCodeBase(), "pic2.gif");
img3 = getImage(getCodeBase(), "pic3.gif");
mt.addImage(img1 , 0);
mt.addImage(img2 , 0);
mt.addImage(img3 , 0);
В качестве первого параметра методу addImage передается ссылка на изображение, загрузку которого необходимо отслеживать, а в качестве второго - идентификатор, который можно будет использовать в процессе отслеживания. Если все, что вам нужно, это дождаться окончания загрузки изображений, то для второго параметра вы можете указать нулевое значение.
Другие методы класса MediaTracker
Какие другие полезные методы, кроме методов addImage и waitForAll есть в классе MediaTracker?
public boolean waitForAll(long ms);
Метод waitForAll с параметром ms позволяет выполнять ожидание в течение заданного времени. Время ожидания задается в миллисекундах. При этом если за указанное время все изображения были успешно загружены, метод waitForAll возвращает значение true, если нет - false.
Вариант метода checkAll с параметром load позволяет проверить, завершилась ли загрузка отслеживаемых изображений:
public boolean checkAll(boolean load);
Если значение параметра load равно true, метод инициирует загрузку изображений.
Если при добавлении изображений методом addImage вы использовали второй параметр этого метода для присваивания разным группам изображений различные идентификаторы, то с помощью метода checkID можно дождаться завершения загрузки отдельной группы изображений:
public boolean checkID(int id);
Есть также вариант этого метода, позволяющий проверить загрузку группы изображений с заданным идентификатором:
public boolean checkID(int id, boolean load);
Метод waitForID с параметрами id и ms позволяет выполнять ожидание загрузки группы изображений с заданным идентификатором в течении указанного периода времени:
public boolean waitForID(int id, long ms);
Класс MediaTracker предоставляет также возможность прослеживать сам процесс загрузки всех добавленных в него изображений или отдельных групп изображений с помощью методов statusAll и statusID:
public int statusAll(boolean load);
public int statusID(int id, boolean load);
В зависимости от значения параметра load эти методы могут инициировать загрузку изображений. Если параметр равен true, загрузка изображений инициируется, если false - выполняется только проверка текущего состояния загрузки.
Методы statusAll и statusID возвращают значение, составленное из отдельных битов состояния при помощи логической операции ИЛИ. Ниже мы перечислили эти биты состояния и привели их краткое описание.
Биты состояния
Описание
MediaTracker.LOADING
Один или несколько отслеживаемых файлов продолжают загружаться
MediaTracker.ABORTED
Загрузка одного или нескольких файлов была прервана
MediatTracker.ERRORED
При загрузке одного или нескольких файлов произошла ошибка
MediaTracker.COMPLETE
Загрузка всех отслеживаемых файлов произошла полностью и успешно
Еще четыре метода, определенных в классе MediaTracker, связаны с обработкой ошибок:
public boolean isErrorAny();
public boolean isErrorID(int id);
public Object[] getErrorsAny();
public Object[] getErrorsID(int id);
Методы isErrorAny и isErrorID позволяют проверить, возникла ли ошибка при загрузке, соответственно, любого из отслеживаемых изображений или изображений из заданной группы. Если ошибка произошла, возвращается значение true, если нет - значение false.
Методы getErrorsAny и getErrorsID возвращают массив объектов, при ожидании загрузки которых произошла ошибка. Первый из этих методов возвращает массив для всех отслеживаемых объектов, второй - только объектов из заданной группы.
Главный класс комбинированного приложения
Класс Combi создан на базе класса Applet, что необходимо для обеспечения работы этого приложения под управлением навигатора Internet:
public class Combi extends Applet
{
. . .
public static void main(String args[])
{
. . .
}
public String getAppletInfo()
{
. . .
}
public void init()
{
. . .
}
public void paint(Graphics g)
{
. . .
}
}
Обратите внимание, что наряду с методами, которые обычно определяются аплетами, такими как getAppletInfo, init и paint, в классе комбинированного приложения определен метод main. Если приложение запускается как аплет, метод main не получает управления. Если же приложение запущено автономно, этот метод первым получает управление и выполняет все необходимые инициализирующие действия.
Главное из этих действий - создание окна на базе класса Frame для размещения в нем аплета, создание аплета и вызов функций инициализации аплета. Ниже мы привели исходный текст метода main приложения Combi, созданный для нас системой Java Applet Wizard:
public static void main(String args[])
{
CombiFrame frame = new CombiFrame("Combi");
frame.show();
frame.hide();
frame.resize(
frame.insets().left + frame.insets().right + 320,
frame.insets().top + frame.insets().bottom + 240);
Combi applet_Combi = new Combi();
frame.add("Center", applet_Combi);
. . .
applet_Combi.init();
applet_Combi.start();
frame.show();
}
Прежде всего, метод main создает объект frame класса CombiFrame, определенного в нашем приложении на базе класса Frame (окно фрейма). Напомним, что класс Frame, который был нами описан в 30 томе “Библиотеки системного программиста”, позволяет приложениям Java создавать окна, напоминающие окна обычных приложений Windows.
Метод show отображает окно фрейма.
Далее в методе main выполняется изменение размеров окна фрейма, перед чем окно скрывается методом hide. Для изменения размеров окна применяется метод resize, которому через первый и второй параметры передаются новые значения, соответственно, ширины и высоты окна.
Инициализация клиента
Процесс инициализации клиентского приложения выглядит весьма просто. Клиент должен просто создать сокет как объект класса Socket, указав адрес IP серверного приложения и номер порта, используемого сервером:
Socket s;
s = new Socket("localhost",9999);
Здесь в качестве адреса IP мы указали специальный адрес localhost, предназначенный для тестирования сетевых приложений, а в качестве номера порта - ззначение 9999, использованное сервером.
Теперь можно создавать входной и выходной потоки. На стороне клиента эта операция выполняется точно также, как и на стороне сервера:
InputStream is;
OutputStream os;
is = s.getInputStream();
os = s.getOutputStream();
Инициализация сервера
Вначале мы рассмотрим действия приложения, которое на момент инициализации является сервером.
Первое, что должно сделать серверное приложение, это создать объект класса ServerSocket, указав конструктору этого класса номер используемого порта:
ServerSocket ss;
ss = new ServerSocket(9999);
Заметим, что объект класса ServerSocket вовсе не является сокетом. Он предназначен всего лишь для установки канала связи с клиентским приложением, после чего создается сокет класса Socket, пригодный для передачи данных.
Установка канала связи с клиентским приложением выполняется при помощи метода accept, определенного в классе ServerSocket:
Socket s;
s = ss.accept();
Метод accept приостанавливает работу вызвавшей его задачи до тех пор, пока клиентское приложение не установит канал связи с сервером. Если ваше приложение однозадачное, его работа будет блокирована до момента установки канала связи. Избежать полной блокировки приложения можно, если выполнять создание канала передачи данных в отдельной задаче.
Как только канал будет создан, вы можете использовать сокет сервера для образования входного и выходного потока класса InputStream и OutputStream, соответственно:
InputStream is;
OutputStream os;
is = s.getInputStream();
os = s.getOutputStream();
Эти потоки можно использовать таким же образом, что и потоки, связанные с файлами.
Обратите также внимание на то, что при создании серверного сокета мы не указали адрес IP и тип сокета, ограничившись только номером порта.
Что касается адреса IP, то он, очевидно, равен адресу IP узла, на котором запущено приложение сервера. В классе ServerSocket определен метод getInetAddress, позволяющий определить этот адрес:
public InetAddress getInetAddress();
Тип сокета указывать не нужно, так как для работы с датаграммными сокетами предназначен класс DatagramSocket, который мы рассмотрим позже.
Исходные тексты приложения
В листинге 1.1 представлены исходные тексты мультизадачного приложения MultiTask, созданного системой автоматизированной разработки аплетов, слегка измененного и снабженного нашими комментариями. В дальнейшем мы создадим блее сложные мультизадачные аплеты.
Листинг 1.1. Файл MultiTask\MultiTask.java
// =========================================================
// Периодическое обновление окна аплета
// с использованием мультизадачности
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class MultiTask extends Applet implements Runnable
{
// Задача, которая будет обновлять окно аплета
Thread m_MultiTask = null;
// -------------------------------------------------------
// MultiTask
// Конструктор класса MultiTask. Не используется
// -------------------------------------------------------
public MultiTask()
{
}
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MultiTask\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Author: Alexandr Frolov\r\n" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
}
// -------------------------------------------------------
// destroy
// Метод, получающий управление при
// завершении работы аплета
Исходные тексты приложения
Исходный файл приложения Rectangles приведен в листинге 1.3.
Листинг 1.3. Файл Rectangles\Rectangles.java
// =========================================================
// Рисование прямоугольников в отдельной задаче
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.util.*;
public class Rectangles extends Applet implements Runnable
{
// Ссылка на задачу рисования прямоугольников
Thread m_Rectangles = null;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Rectangles\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
g.setColor(Color.black);
// Рисуем рамку вокруг окна аплета
g.drawRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
}
Исходные тексты приложения
Исходный текст приложения вы найдете в листинге 1.5.
Листинг 1.5. Файл MultiTask2\ MultiTask2.java
// =========================================================
// Рисование прямоугольников и эллипсов
// в разных задачах
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// =========================================================
// Основной класс аплета
// =========================================================
public class MultiTask2 extends Applet
{
// Ссылка на задачу рисования прямоугольников
DrawRectangles m_DrawRectThread = null;
// Ссылка на задачу рисования эллипсов
DrawEllipse m_DrawEllipseThread = null;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MultiTask2\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
// Выбираем в контекст отображения черный цвет
Исходные тексты приложения
Исходный текст приложения Scroller представлен в листинге 1.7.
Листинг 1.7. Файл Scroller\Scroller.java
// =========================================================
// Просмотр текста в режиме динамической свертки
// по вертикали
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Scroller extends Applet implements Runnable
{
// Ссылка на задачу, выполняющую свертку
Thread m_Scroller = null;
// Отображаемые строки
private String m_String1 = "Мы представляем наши новые книги";
private String m_String2 =
"Том 29. Сервер Web своими руками";
private String m_String3 =
"Том 30. Microsoft Visual J++. Создание приложений на языке Java. Часть 1";
private String m_String4 =
"Том 31. Разработка приложений для Internet с Visual C++ и MFC";
private String m_String5 =
"Том 32. Microsoft Visual J++. Создание приложений на языке Java. Часть 2";
private String m_String6 = "";
// Имена параметров
private final String PARAM_String1 = "String1";
private final String PARAM_String2 = "String2";
private final String PARAM_String3 = "String3";
private final String PARAM_String4 = "String4";
private final String PARAM_String5 = "String5";
private final String PARAM_String6 = "String6";
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Scroller\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
Исходные тексты приложения
Исходные тексты приложения HorzScroll приведены в листинге 1.9.
Листинг 1.9. Файл HorzScroll\HorzScroll.java
// =========================================================
// Рисование строки текста по буквам
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class HorzScroll extends Applet implements Runnable
{
// Ссылка на задачу рисования строки текста
Thread m_HorzScroll = null;
// Значения параметров по умолчанию
private String m_Str = "Hello from Java!";
private String m_Fnt = "Helvetica";
private String m_style = "BOLD";
private int m_size = 12;
private String m_color = "red";
private int m_delay = 50;
// Имена параметров
private final String PARAM_str = "str";
private final String PARAM_font = "font";
private final String PARAM_style = "style";
private final String PARAM_size = "size";
private final String PARAM_color = "color";
private final String PARAM_delay = "delay";
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: HorzScroll\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// getParameterInfo
// Иинформация о параметрах
// -------------------------------------------------------
Исходные тексты приложения
Исходные тексты приложения Synchro приведены в листинге 1.11.
Листинг 1.11. Файл Synchro\Synchro.javal
// =========================================================
// Демонстрация синхронизации двух задач
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// =========================================================
// Основной класс аплета
// =========================================================
public class Synchro extends Applet
{
// Ссылка на задачу рисования прямоугольников
DrawRectangles m_DrawRectThread = null;
// Ссылка на задачу, периодически разблокирующую задачу
// рисования прямоугольников
NotifyTask m_NotifyTaskThread = null;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Synchro\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения желтый цвет
g.setColor(Color.yellow);
// Закрашиваем внутреннюю область окна аплета
g.fillRect(0, 0,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1);
Исходные тексты приложения
Исходный текст аплета MemStream приведен в листинге 2.3.
Листинг 2.3. Файл MemStream\MemStream.java
// =========================================================
// Работа с потоками в оперативной памяти
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.io.*;
public class MemStream extends Applet
{
// Выходной поток
DataOutputStream OutStream;
// Входной поток
DataInputStream InStream;
// Строка, которая будет записана в поток
String sOut;
// Массив, в который будет копироваться содержимое
// выходного потока
byte[] bMemStream;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: MemStream\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление при инициализации
// аплета
// -------------------------------------------------------
public void init()
{
// Инициализируем строку для записи в поток
sOut = "Hello, Java!";
try
{
// Создаем выходной поток в оперативной памяти
ByteArrayOutputStream baStream =
new ByteArrayOutputStream(255);
// Создаем буферизованный форматированный поток
// на базе потока baStream
OutStream = new DataOutputStream(
new BufferedOutputStream(baStream));
Исходные тексты приложения
Исходные тексты приложения DirectFileAccess приведены в листинге 2.9.
Листинг 2.9. Файл DirectFileAccess\DirectFileAccess.java
// =========================================================
// Прямой доступ к файлу с помощью класса RandomAccessFile
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс DirectFileAccess
// Главный класс приложения
// =========================================================
public class DirectFileAccess
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
try
{
// Создаем новую базу данных
SimpleDBMS db = new SimpleDBMS(
"dbtest.idx", "dbtest.dat");
// Добавляем в нее три записи
db.AddRecord("Ivanov", 1000);
db.AddRecord("Petrov", 2000);
db.AddRecord("Sidoroff", 3000);
// Получаем и отображаем содержимое первых трез
// записей с номерами 2, 1 и 0
System.out.println(db.GetRecordByNumber(2));
System.out.println(db.GetRecordByNumber(1));
System.out.println(db.GetRecordByNumber(0));
// Закрываем базу данных
db.close();
// После ввода любой строки завершаем работу программы
System.out.println("Press to terminate...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
Исходные тексты приложения
Исходные тексты приложения ShowChart приведены в листинге 3.3.
Листинг 3.3. Файл ShowChart\ShowChart.java
// =========================================================
// Рисование круговой диаграммы, данные для которой
// получены от сервера Web через сеть
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;
public class ShowChart extends Applet
{
// Адрес URL файла с данными для круговой диаграммы
URL SrcURL;
// Содержимое этого файла
Object URLContent;
// Код ошибки
int errno = 0;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ShowChart\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод, получающий управление при инициализации аплета
// -------------------------------------------------------
public void init()
{
try
{
// Создаем объект класса URL для файла с данными
// для круговой диаграммы
SrcURL = new URL("http://frolov/chart.txt");
try
{
// Получаем содержимое этого файла
URLContent = SrcURL.openConnection().getContent();
}
catch (IOException ioe)
{
showStatus("getContent exception");
// При возникновении исключения во время получения
Исходные тексты приложения
Исходный текст приложения ImageDraw вы найдете в листинге 4.1.
Листинг 4.1. Файл ImageDraw\ImageDraw.java
// =========================================================
// Рисование растровых изображений
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ImageDraw extends Applet
{
// Изображение флоппи-диска
Image FloppyDiskImg;
// Изображение компакт-диска
Image CDDiskImg;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ImageDraw\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление
// при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Загружаем изображение флоппи-диска
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
// Загружаем изображение флоппи-диска
CDDiskImg = getImage(getCodeBase(), "cd.gif");
}
// -------------------------------------------------------
// paint
// Метод paint, выполняющий рисование в окне аплета
// -------------------------------------------------------
public void paint(Graphics g)
{
// Определяем текущие размеры окна аплета
Dimension dimAppWndDimension = size();
// Выбираем в контекст отображения белый цвет
Исходные тексты приложения
Исходные тексты приложения ImageDrawWait вы найдете в листинге 4.3.
Листинг 4.3. Файл ImageDrawWait\ImageDrawWait.java
// =========================================================
// Рисование растровых изображений с ожиданием их загрузки
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class ImageDrawWait extends Applet
{
// Фоновое изображение
Image BkgImg;
// Изображение флоппи-диска
Image FloppyDiskImg;
// Изображение компакт-диска
Image CDDiskImg;
// Ссылка на MediaTracker
MediaTracker mt;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: ImageDrawWait\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление
// при инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем объект класса MediaTracker
mt = new MediaTracker(this);
// Загружаем фоновое изображение
BkgImg = getImage(getCodeBase(), "bkg.gif");
// Добавляем его в список объекта MediaTracker
mt.addImage(BkgImg , 0);
// Загружаем изображение флоппи-диска
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
// Добавляем его в список объекта MediaTracker
mt.addImage(FloppyDiskImg, 0);
Исходные тексты приложения
Главный файл исходных текстов приложения DrawImageObserver приведен в листинге 4.5.
Листинг 4.5. Файл DrawImageObserver\DrawImageObserver.java
// =========================================================
// Рисование растровых изображений с ожиданием их загрузки
// Для ожидания применяется интерфейс ImageObserver
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class DrawImageObserver extends Applet
{
// Фоновое изображение
Image BkgImg;
boolean fAllLoaded = false;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: DrawImageObserver\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Метод init, получает управление при
// инициализации аплета
// -------------------------------------------------------
public void init()
{
// Загружаем фоновое изображение
BkgImg = getImage(getCodeBase(), "bkg.gif");
}
// -------------------------------------------------------
// imageUpdate
// Вызывается, когда появляется информация об изображении
// -------------------------------------------------------
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h)
{
// Проверяем, все ли изображение загружено
fAllLoaded = ((flags & ALLBITS) != 0);
// Если все, перерисовываем окно
Исходные тексты приложения
Главный файл исходных текстов приложения CDRotation представлен в листинге 4.7.
Листинг 4.7. Файл CDRotation\CDRotation.java
// =========================================================
// Рисование вращающегося компакт-диска
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class CDRotation extends Applet implements Runnable
{
// Ссылка на задачу рисования
// вращающегося компакт-диска
Thread m_CDRotation = null;
// Контекст отображения для рисования
private Graphics m_Graphics;
// Массив изображений компакт-диска
private Image m_Images[];
// Номер текущего изображения
private int m_nCurrImage;
// Ширина изображения
private int m_nImgWidth = 0;
// Высота изображения
private int m_nImgHeight = 0;
// Флаг загрузки всех изображений
private boolean m_fAllLoaded = false;
// Общее количество изображений
private final int NUM_IMAGES = 11;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: CDRotation\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// displayImage
// Рисование текущего изображения, если все изображения
// уже загружены
// -------------------------------------------------------
private void displayImage(Graphics g)
{
// Если не все изображения загружены,
Исходные тексты приложения
Основной файл исходных текстов приложения приведен в листинге 5.1.
Листинг 5.1. Файл Audio\ Audio.java
// =========================================================
// Проигрывание аудиофайла
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
public class Audio extends Applet
{
// -------------------------------------------------------
// Поля класса.
// Создаются автоматически для всех параметров аплета
// -------------------------------------------------------
// Строка для хранения имени аудиофайла
private String m_ClipName = "kaas.au";
// -------------------------------------------------------
// Имена параметров
// -------------------------------------------------------
// Строка имени параметра
private final String PARAM_ClipName = "ClipName";
// Аудиоклип
AudioClip auClip;
// Кнопка для однократного проигрывания
Button btPlay;
// Кнопка для проигрывания в цикле
Button btLoop;
// Кнопка для остановки проигрывания
Button btStop;
// Флаг проигрывания в цикле
boolean fLoopPlay = false;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Audio\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// getParameterInfo
// Метод, возвращающий описание параметров
Исходные тексты приложения
Основной файл исходных текстов вы найдете в листинге 6.1.
Листинг 6.1. Файл Inspector\Inspector.java
// =========================================================
// Аплет, который получает список всех аплетов
// и управляет аплетом Audio
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
import java.util.*;
// Импортируем класс Audio, так как нам нужен доступ к
// его полям
import Audio;
public class Inspector extends Applet
{
// Контекст аплетов
AppletContext appContext;
// Список аплетов, расположенных в документе HTML
Enumeration eApplets;
// Ссылка на аплет Audio
Audio appAudio = null;
// Кнопка для однократного проигрывания
Button btPlay;
// Кнопка для проигрывания в цикле
Button btLoop;
// Кнопка для остановки проигрывания
Button btStop;
// -------------------------------------------------------
// getAppletInfo
// Метод, возвращающей строку информации об аплете
// -------------------------------------------------------
public String getAppletInfo()
{
return "Name: Inspector\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
// -------------------------------------------------------
// init
// Вызывается во время инициализации аплета
// -------------------------------------------------------
public void init()
{
// Создаем кнопку для однократного проигрывания
btPlay = new Button("Play");
// Создаем кнопку для проигрывания в цикле
btLoop = new Button("Loop");
Исходные тексты приложения
Исходные текст основного класса приложения Combi представлен в листинге 7.1.
Листинг 7.1. Файл Combi\Combi.java
// =========================================================
// Приложение, способное работать как аплет, включенный
// в документ HTML, а также как автономное приложение Java
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.applet.*;
import java.awt.*;
// Импортируем класс CombiFrame
import CombiFrame;
// =========================================================
// Combi
// Основной класс приложения
// =========================================================
public class Combi extends Applet
{
// Режим работы приложения. Если это поле содержит
// значение true, приложение работает автономно, если
// false - в составе документа HTML под управлением
// навигатора
boolean m_fStandAlone = false;
// -------------------------------------------------------
// main
// Метод main получает управление, когда приложение
// работает автономно
// -------------------------------------------------------
public static void main(String args[])
{
// Создаем окно класса CombiFrame, унаследованного от
// класса Frame (окно фрейма)
CombiFrame frame = new CombiFrame("Combi");
// Отображаем окно фрейма
frame.show();
// Выполняем изменение размеров окна
// Скрываем окно фрейма
frame.hide();
// Изменяем размеры окна фрейма
frame.resize(
frame.insets().left + frame.insets().right + 320,
frame.insets().top + frame.insets().bottom + 240);
// Создаем аплет класса Combi
Combi applet_Combi = new Combi();
// Добавляем окно этого аплета в окно фрейма
frame.add("Center", applet_Combi);
Исходные тексты приложения InetAddressDemo
Исходные тексты приложения InetAddressDemo приведены в листинге 3.1.
Листинг 3.1. Файл InetAddressDemo\InetAddressDemo.java
// =========================================================
// Работа с адресами IP с помощью класса InetAddress
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.net.*;
import java.io.*;
import java.util.*;
// =========================================================
// Класс InetAddressDemo
// Главный класс приложения
// =========================================================
public class InetAddressDemo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sIn;
// Рабочая строка
String str;
// Адрес локального узла
InetAddress iaLocal;
// Массив байт адреса локального узла
byte[] iaLocalIP;
// Массив всех адресов удаленного узла
InetAddress[] iaRemoteAll;
try
{
// Получаем адрес локального узла
iaLocal = InetAddress.getLocalHost();
// Отображаем имя локального узла на консоли
System.out.println("Local host name: " +
iaLocal.getHostName());
// Определяем адрес IP локального узла
iaLocalIP = iaLocal.getAddress();
// Отображаем отдельные байты адреса IP
// локального узла
System.out.println("Local host IP address: " +
(0xff & (int)iaLocalIP[0]) + "." +
(0xff & (int)iaLocalIP[1]) + "." +
Исходные тексты программы CGI
В лситинге 3.10 мы привели исходный текст программы CGI с именем controls. Он несколько упрощен по сравнению с исходным текстом одноименного приложения, описанного в 29 томе “Библиотеки системного программиста” - мы выбросили обработку метода передачи данных GET, так как наше приложение CallCGI передает данные только методом POST. Описание этой программы вы найдете в упомянутом 29 томе.
Листинг 3.10. Файл controls\controls.c
// ===============================================
// Программа CGI controls.c
// Демонстрирует методы получения и обработки
// данных от форм, расположенных в документах HTML
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// ===============================================
#include
#include
#include
// Прототипы функций перекодировки
void DecodeStr(char *szString);
char DecodeHex(char *str);
// ------------------------------------------------
// Функция main
// Точка входа программы CGI
// ------------------------------------------------
void main(int argc, char *argv[])
{
int lSize;
FILE * fileReceived;
char * szMethod;
char szBuf[8196];
char szSrcBuf[8196];
char * szPtr;
char * szParam;
// Вывод заголовка HTTP и разделительной строки
printf("Content-type: text/html\n\n");
// Вывод начального форагмента документа HTML,
// формируемого динамически
printf("");
printf("
Call CGI from Java"
" ");
// Определяем метод передачи данных
szMethod = getenv("REQUEST_METHOD");
// Обработка метода POST
if(!strcmp(szMethod, "POST"))
{
// Определяем размер данных, полученных от навигатора
// при передаче данных из полей формы
lSize = atoi(getenv("CONTENT_LENGTH"));
// Читаем эти данные в буфер szBuf из
// стандартного потока ввода STDIN
fread(szBuf, lSize, 1, stdin);
// Создаем файл, в который будут записаны
// принятые данные
fileReceived = fopen("received.dat", "w");
// Выполняем запись принятых данных
fwrite(szBuf, lSize, 1, fileReceived);
// Закрываем файл принятых данных
fclose(fileReceived);
// Отображаем значения некоторых переменных среды
printf("Environment variables ");
// Метод доступа
printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD"));
// Размер полученных данных в байтах
printf(" CONTENT_LENGTH = %ld", lSize);
// Тип полученных данных
printf(" CONTENT_TYPE = %s", getenv("CONTENT_TYPE"));
// Закрываем буфер данных двоичным нулем,
// превращая его таким образом в строку
szBuf[lSize] = '\0';
// Делаем копию принятых данных в буфер szSrcBuf
strcpy(szSrcBuf, szBuf);
// Отображаем принятые данные без обработки
printf("Received data ");
printf("%s", szSrcBuf);
// Выполняем перекодировку принятых данных
DecodeStr(szSrcBuf);
// Отображаем результат перекодировки
printf("Decoded data ");
printf("%s", szSrcBuf);
// Выводим список значений полей формы
printf("Filds list ");
// Дописываем в конец буфера принятых данных
// символ "&", который используется в качестве
// разделителя значений полей
szBuf[lSize] = '&';
szBuf[lSize + 1] = '\0';
// Цикл по полям формы
for(szParam = szBuf;;)
{
// Ищем очередной разделитель
szPtr = strchr(szParam, '&');
// Если он найден, раскодируем строку параметров
if(szPtr != NULL)
{
*szPtr = '\0';
DecodeStr(szParam);
// Выводим в документ значение параметра
printf("%s ", szParam);
// Переходим к следующему параметру
szParam = szPtr + 1;
// Если достигнут конец буфера, завершаем цикл
if(szParam >= (szBuf + lSize))
break;
}
else
break;
}
// Выводим завершающий фрагмент документа HTML
printf("");
return;
}
}
// ------------------------------------------------
// Функция DecodeStr
// Раскодирование строки из кодировки URL
// ------------------------------------------------
void DecodeStr(char *szString)
{
int src;
int dst;
char ch;
// Цикл по строке
for(src=0, dst=0; szString[src]; src++, dst++)
{
// Получаем очередной символ перекодируемой строки
ch = szString[src];
// Заменяем символ "+" на пробел
ch = (ch == '+') ? ' ' : ch;
// Сохраняем результат
szString[dst] = ch;
// Обработка шестнадцатеричных кодов вида "%xx"
if(ch == '%')
{
// Выполняем преобразование строки "%xx"
// в код символа
szString[dst] = DecodeHex(&szString[src + 1]);
src += 2;
}
}
// Закрываем строку двоичным нулем
szString[dst] = '\0';
}
// ------------------------------------------------
// Функция DecodeHex
// Раскодирование строки "%xx"
// ------------------------------------------------
char DecodeHex(char *str)
{
char ch;
// Обрабатываем старший разряд
if(str[0] >= 'A')
ch = ((str[0] & 0xdf) - 'A') + 10;
else
ch = str[0] - '0';
// Сдвигаем его влево на 4 бита
ch <<= 4;
// Обрабатываем младший разряд и складываем
// его со старшим
if(str[1] >= 'A')
ch += ((str[1] & 0xdf) - 'A') + 10;
else
ch += str[1] - '0';
// Возвращаем результат перекодировки
return ch;
}
Исходный текст клиентского приложения SocketClient
Исходный текст клиентского приложения SocketClient приведен в листинге3.6.
Листинг 3.6. Файл SocketClient\SocketClient.java
// =========================================================
// Использование потоковых сокетов.
// Приложение клиента
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class SocketClient
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Сокет для связи с сервером
Socket s;
// Входной поток для приема данных от сервера
InputStream is;
// Выходной поток для передачи данных серверу
OutputStream os;
try
{
// Выводим строку приглашения
System.out.println("Socket Client Application" +
"\nEnter any string or 'quit' to exit...");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Открываем сокет
s = new Socket("localhost",9999);
// Создаем входной поток для приема данных от сервера
is = s.getInputStream();
// Создаем выходной поток для передачи данных серверу
os = s.getOutputStream();
// Буфер для передачи данных
byte buf[] = new byte[512];
// Размер принятого блока данных
int length;
// Рабочая строка
String str;
// Вводим команды и передаем их серверу
while(true)
{
// Читаем строку команды с клавиатуры
length = System.in.read(bKbdInput);
// Если строка не пустая, обрабатываем ее
if(length != 1)
{
// Преобразуем строку в формат String
str = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\n");
str = new String((String)st.nextElement());
// Выводим передаваемую строку команды
// на консоль для контроля
System.out.println("> " + str);
// Записываем строку в выходной поток,
// передавая ее таким образом серверу
os.write(bKbdInput, 0, length);
// Сбрасываем буфер выходного потока
os.flush();
// Принимаем ответ сервера
length = is.read(buf);
if(length == -1)
break;
// Отображаем принятую строку на консоли
str = new String(buf, 0);
st = new StringTokenizer(str, "\n");
str = new String((String)st.nextElement());
System.out.println(">> " + str);
// Если введена строка 'quit', завершаем
// работу приложения
if(str.equals("quit"))
break;
}
}
// Закрываем входной и выходной потоки
is.close();
os.close();
// Закрываем сокет
s.close();
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
try
{
System.out.println(
"Press to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст приложения
Исходный текст приложения приведен в листинге 2.1.
После трансляции исходного текста вы можете запустить его на выполнение непосредственно из среды разработки приложений Microsoft Visual J++. При этом, когда на экране появится диалоговая панель Information For Running Class (рис. 2.5), вы должны указать в поле Class file name имя Standard, а в поле Run project under включить переключатель Stand-alone interpreter.
Рис. 2.5. Заполнение диалоговой панели Information For Running Class
При этом приложение будет запущено под управлением автономного интерпретатора Java jview.exe.
Листинг 2.1. Файл Standard\Standard.java
// =========================================================
// Демонстрация использования стандартных потоков
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
public class Standard
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sOut;
// Выполняем попытку вывода на консоль
// строки приглашения
try
{
// Выводим строку приглашения
System.out.println("Hello, Java!\n" +
"Enter string and press ...");
// Читаем с клавиатуры строку
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
// Выводим только что введенную строку на консоль
System.out.println(sOut);
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.err.println(ioe.toString());
}
// Ожидаем ввода с клавиатуры, а затем завершаем
// работу приложения
try
{
System.out.println(
"Press to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.err.println(ioe.toString());
}
}
}
Исходный текст приложения
Исходный текст приложения StreamDemo представлен в листинге 2.2.
Листинг 2.2. Файл StreamDemo\StreamDemo.java
// =========================================================
// Демонстрация использования потоков для работы с файлами
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
public class StreamDemo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Выходной поток
DataOutputStream OutStream;
// Входной поток
DataInputStream InStream;
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка, которая будет записана в поток
String sOut;
// Выполняем попытку вывода на консоль строки
// приглашения
try
{
// Выводим строку приглашения
System.out.println("Hello, Java!\n" +
"Enter string and press ...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
// Выполняем попытку записи в выходной поток
try
{
// Создаем выходной буферизованный поток данных
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
// Записываем строку sOut в выходной поток
OutStream.writeBytes(sOut);
Исходный текст приложения
Исходный текст приложения StreamToken представлен в листинге 2.5.
Листинг 2.5. Файл StreamToken\StreamToken.java
// =========================================================
// Разбор входного потока при помощи класса
// StreamTokenizer
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
// =========================================================
// Класс StreamToken
// Главный класс приложения
// =========================================================
public class StreamToken
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Выходной поток
DataOutputStream OutStream;
// Входной поток
DataInputStream InStream;
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка, которая будет записана в поток
String sOut;
try
{
// Выводим строку приглашения
System.out.println("Enter string to parse...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
// Создаем выходной буферизованный поток данных
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
// Записываем строку sOut в выходной поток
OutStream.writeBytes(sOut);
// Закрываем выходной поток
OutStream.close();
// Создаем входной буферизованный поток данных
InStream = new DataInputStream(
Исходный текст приложения
Исходный текст приложения StringToken вы найдете в листинге 2.6.
Листинг 2.6. Файл StringToken\StringToken.java
// =========================================================
// Разбор текстовой строки при помощи класса
// StringTokenizer
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс StringToken
// Главный класс приложения
// =========================================================
public class StringToken
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка, которая будет записана в поток
String sOut;
String str;
try
{
// Выводим строку приглашения
System.out.println("Enter string to parse...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sOut = new String(bKbdInput, 0);
// Создаем разборщик текстовой строки
StringTokenizer st;
st = new StringTokenizer(sOut, ",.; ");
// Запускаем цикл разборки строки
while(st.hasMoreElements())
{
// Получаем очередной жлемент
str = new String((String)st.nextElement());
// Записываем его в стандартный поток вывода
System.out.println(str);
}
System.out.println("Press to terminate...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст приложения
Исходный текст приложения DirList представлен в листинге 2.8.
Листинг 2.8. Файл DirList\DirList.java
// =========================================================
// Просмотр содержимого каталога при помощи класса File
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс DirList
// Главный класс приложения
// =========================================================
public class DirList
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Путь к каталогу, содержимое которого
// мы будем просматривать
String sDirPath;
// Маска для просмотра
String sMask;
// Массив строк содержимого каталога
String[] dirlist;
try
{
// Выводим строку приглашения для ввода пути
// к каталогу, содержимое которого будем просматривать
System.out.println("Enter directory path...");
System.in.read(bKbdInput);
sDirPath = new String(bKbdInput, 0);
StringTokenizer st;
st = new StringTokenizer(sDirPath, "\r\n");
sDirPath = new String((String)st.nextElement());
// Вводим строку маски
System.out.println("Enter mask...");
System.in.read(bKbdInput);
sMask = new String(bKbdInput, 0);
st = new StringTokenizer(sMask, "\r\n");
sMask = new String((String)st.nextElement());
// Создаем объект класса File, соответствующий
Исходный текст приложения
Исходный текст приложения URLDemo приведены в листинге 3.2.
Листинг 3.2. Файл URLDemo\URLDemo.java
// =========================================================
// Копирование файла, расположенного в каталоге
// сервера Web, с помощью класса URL
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.net.*;
import java.io.*;
import java.util.*;
// =========================================================
// Класс InetAddressDemo
// Главный класс приложения
// =========================================================
public class URLDemo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sIn;
// Строка адреса URL
String sURL;
// Адрес URL удаленного узла
URL u;
// Рабочий буфер для копирования файла
byte buf[] = new byte[1024];
try
{
// Вводим адрес URL удаленного узла
System.out.println("Enter remote host name...");
System.in.read(bKbdInput);
sIn = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(sIn, "\r\n");
sURL = new String((String)st.nextElement());
// Создаем объект класса URL
u = new URL(sURL);
// Создаем входной поток, связанный с объектом,
// адрес URL которого хранится в поле u
InputStream is = u.openStream();
// Создаем выходной буферизованный форматированный
Исходный текст приложения CallCGI
Исходный текст приложения CallCGI приведен в листинге 3.9.
Листинг 3.9. Файл CallCGI\CallCGI.java
// =========================================================
// Вызов расширения сервера Web на базе интерфейса CGI
// из приложения Java
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class CallCGI
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Размер принятого блока данных
int length;
// Рабочая строка
String str;
// Адрес URL вызываемой программы CGI
URL u;
// Канал связи с расширением CGI
URLConnection c;
// Выходной поток для передачи данных расширению CGI
PrintStream ps;
// Входной поток для получения данных от расширения CGI
DataInputStream is;
try
{
// Выводим строку приглашения
System.out.println("CGI extension call" +
"\nEnter any string for send to CGI...");
// Читаем строку, передаваемую расширению,
// с клавиатуры
length = System.in.read(bKbdInput);
// Если строка не пустая, обрабатываем ее
if(length != 1)
{
// Преобразуем строку в формат String
str = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\n");
str = new String((String)st.nextElement());
// Выполняем кодировку URL для передаваемой строки
String StrEncoded = URLEncoder.encode(str);
// Отображаем перекодированную строку
System.out.println("Encoded string: >" +
StrEncoded + "<");
// Создаем объект класса URL для расширения CGI
u = new URL(
"http://frolov/frolov-cgi/controls.exe");
// Открываем канал связи с расширением CGI
c = u.openConnection();
// Создаем выходной поток данных для передачи
// введенной строки серверу CGI
ps = new PrintStream(c.getOutputStream());
// Передаем закодированную строку расширению CGI
ps.println(StrEncoded);
// Закрываем выходной поток
ps.close();
// Создаем входной поток для приема данных от
// расширения CGI
is = new DataInputStream(c.getInputStream());
System.out.println(
"\n------------------------------------------" +
"\n Data from CGI extension" +
"\n------------------------------------------\n");
// Прием данных выполняем в цикле
while (true)
{
// Получаем очередную строку
str = is.readLine();
// Если последняя строка, прерываем цикл
if(str == null)
break;
// Отображаем принятую строку
System.out.println(str);
}
// Закрываем входной поток
is.close();
}
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
try
{
System.out.println(
"Press to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString()); }
}
}
Исходный текст приложения DatagramClient
В листинге 3.8 приведен исходный текст приложения DatagramClient.
Листинг 3.8. Файл DatagramClient\DatagramClient.java
// =========================================================
// Использование датаграммных сокетов
// Приложение клиента
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class DatagramClient
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Размер введенной строки
int length;
// Рабочая строка
String str;
// Сокет клиента
DatagramSocket s;
// Передаваемый пакет
DatagramPacket pout;
try
{
// Выводим строку приглашения
System.out.println(
"Datagram Socket Client Application" +
"\nEnter any string or 'quit' to exit...");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Получаем адрес локального узла
InetAddress OutAddress = InetAddress.getLocalHost();
// Создаем сокет с использованием любого
// свободного порта
s = new DatagramSocket();
// Создаем передаваемый пакет
pout = new DatagramPacket(bKbdInput, bKbdInput.length,
OutAddress, 9998);
// Цикл передачи команд серверу
while(true)
{
// Читаем строку команды с клавиатуры
length = System.in.read(bKbdInput);
// Если строка не пустая, обрабатываем ее
if(length != 1)
{
// Преобразуем строку в формат String
str = new String(bKbdInput, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\n");
str = new String((String)st.nextElement());
// Выводим передаваемую строку команды
// на консоль для контроля
System.out.println("> " + str);
// Посылаем пакет серверу
s.send(pout);
// Если введена команда 'quit', прерываем цикл
if(str.equals("quit"))
break;
}
}
// Закрываем сокет
s.close();
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
try
{
System.out.println(
"Press to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст приложения DatagramServer
Исходный текст приложения DatagramServer вы найдете в листинге 3.7.
Листинг 3.7. Файл DatagramServer\DatagramServer.java
// =========================================================
// Использование датаграммных сокетов
// Приложение сервера
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class DatagramServer
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Буфер для чтения команд
byte buf[] = new byte[512];
// Сокет сервера
DatagramSocket s;
// Принимаемый пакет
DatagramPacket pinp;
// Адрес узла, откуда пришел принятый пакет
InetAddress SrcAddress;
// Порт, откуда пришел принятый пакет
int SrcPort;
try
{
// Выводим строку приглашения
System.out.println(
"Datagramm Socket Server Application");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Создаем сокет сервера
s = new DatagramSocket(9998);
// Создаем пакет для приема команд
pinp = new DatagramPacket(buf, 512);
// Цикл обработки команд, полученных от клиента
while(true)
{
// Принимаем пакет от клиента
s.receive(pinp);
// Получаем адрес узла, приславшего пакет
SrcAddress = pinp.getAddress();
// Получаем порт, на котором был передан пакет
SrcPort = pinp.getPort();
// Отображаем принятую команду на консоли сервера
// Формируем строку из принятого блока
String str = new String(buf, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\r\n");
str = new String((String)st.nextElement());
// Выводим строку команды на консоль
System.out.println("> " + str + " < " +
"port: " + SrcPort);
// Если пришла команда 'quit', прерываем цикл
if(str.equals("quit"))
break;
}
// Закрываем сокет сервера
s.close();
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
try
{
System.out.println(
"Press to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст приложения FileInfo
Исходный текст приложения FileInfo приведен в листинге 2.7.
Листинг 2.7. Файл FileInfo\FileInfo.java
// =========================================================
// Просмотр атрибутов файла при помощи класса File
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.util.*;
// =========================================================
// Класс FileInfo
// Главный класс приложения
// =========================================================
public class FileInfo
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Введенная строка
String sFilePath;
try
{
// Выводим строку приглашения
System.out.println("Enter file path...");
// Читаем с клавиатуры строку для записи в файл
System.in.read(bKbdInput);
// Преобразуем введенные символы в строку типа String
sFilePath= new String(bKbdInput, 0);
// Отбрасываем символ конца строки
StringTokenizer st;
st = new StringTokenizer(sFilePath, "\r\n");
sFilePath = new String((String)st.nextElement());
// Создаем объект класса File, соответствующий
// введенному пути
File fl = new File(sFilePath);
// Если указанный файл или каталог не существует,
// выводим сообщение и завершаем работу
if(!fl.exists())
{
System.out.println("File not found: " + sFilePath);
}
// Если путь существует, определяем параметры
// соответствующего файла или каталога
else
{
// Проверяем, был указан файл или каталог
if(fl.isDirectory())
System.out.println("File " + sFilePath +
" is directory");
else if (fl.isFile())
System.out.println("File " + sFilePath +
" is file");
// Получаем и выводим атрибуты файла или каталога
System.out.println(
"Parent: " + fl.getParent() +
"\nLength: " + fl.length() +
"\nRead op. available: " + fl.canRead() +
"\nWrite op. available: " + fl.canWrite());
}
System.out.println("Press to terminate...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исходный текст серверного приложения SocketServ
Исходный текст серверного приложения SocketServ приведен в листинге3.5.
Листинг 3.5. Файл SocketServ\SocketServ.java
// =========================================================
// Использование потоковых сокетов.
// Приложение сервера
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// =========================================================
import java.io.*;
import java.net.*;
import java.util.*;
public class SocketServ
{
// -------------------------------------------------------
// main
// Метод, получающий управление при запуске приложения
// -------------------------------------------------------
public static void main(String args[])
{
// Массив для ввода строки с клавиатуры
byte bKbdInput[] = new byte[256];
// Объект класса ServerSocket для создания канала
ServerSocket ss;
// Сокет сервера
Socket s;
// Входной поток для приема команд от клиента
InputStream is;
// Выходной поток для передачи ответа клиенту
OutputStream os;
try
{
System.out.println("Socket Server Application");
}
catch(Exception ioe)
{
// При возникновении исключения выводим его описание
// на консоль
System.out.println(ioe.toString());
}
try
{
// Создаем объект класса ServerSocket
ss = new ServerSocket(9999);
// Ожидаем соединение
s = ss.accept();
// Открываем входной поток для приема
// команд от клиента
is = s.getInputStream();
// Открываем выходной поток для передачи
// ответа клиенту
os = s.getOutputStream();
// Буфер для чтения команд
byte buf[] = new byte[512];
// Размер принятого блока данных
int lenght;
// Цикл обработки команд, полученных от клиента
while(true)
{
// Получаем команду
lenght = is.read(buf);
// Если входной поток исчерпан, завершаем
// цикл обработки команд
if(lenght == -1)
break;
// Отображаем принятую команду на консоли сервера
// Формируем строку из принятого блока
String str = new String(buf, 0);
// Обрезаем строку, удаляя символ конца строки
StringTokenizer st;
st = new StringTokenizer(str, "\r\n");
str = new String((String)st.nextElement());
// Выводим строку команды на консоль
System.out.println("> " + str);
// Посылаем принятую команду обратно клиенту
os.write(buf, 0, lenght);
// Сбрасываем буфер выходного потока
os.flush();
}
// Закрываем входной и выходной потоки
is.close();
os.close();
// Закрываем сокет сервера
s.close();
// Закрываем соединение
ss.close();
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
try
{
System.out.println(
"Press to terminate application...");
System.in.read(bKbdInput);
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
}
Исключения при создании потоков
При создании потоков на базе классов FileOutputStream и FileInputStream могут возникать исключения FileNotFoundException, SecurityException, IOException.
Исключение FileNotFoundException возникает при попытке открыть входной поток данных для несуществующего файла, то есть когда файл не найден.
Исключение SecurityException возникает при попытке открыть файл, для которого запрещен доступ. Например, если файл можно только читать, а он открывается для записи, возникнет исключение SecurityException.
Если файл не может быть открыт для записи по каким-либо другим причинам, возникает исключение IOException.
Использование датаграммных сокетов
Как мы уже говорили, датаграммные сокеты не гарантируют доставку пакетов данных. Тем не менее, они работают быстрее потоковых и обеспечивают возможность широковещательной расслыки пакетов данных одновременно всем узлам сети. Последняя возможность используется не очень широко в сети Internet, однако в корпоративной сети Intranet вы вполне можете ей воспользоваться.
Для работы с датаграммными сокетами приложение должно создать сокет на базе класса DatagramSocket, а также подготовить объект класса DatagramPacket, в который будет записан принятый от партнера по сети блок данных.
Канал, а также входные и выходные потоки создавать не нужно. Данные передаются и принимаются методами send и receive, определенными в классе DatagramSocket.
Использование интерфейса AppletContext
Для того чтобы получить список всех аплетов, расположенных в текущем документе HTML, ваш аплет прежде всего должен получить ссылку на интерфейс AppletContext. Затем нужно вызвать метод getApplets, возвращающий искомый список.
Рассмотрим этот процесс подробно.
Как связаться с авторами
Полную информацию о всех наших книгах серий “Библиотека системного программиста” и “Персональный компьютер. Шаг за шагом”, а также дискеты к книгам, статьи и другую информацию вы можете найти в сети Internet на серверах Web по следующим адресам:
http://www.glasnet.ru/~frolov
http://www.dials.ccas.ru/frolov
Вы можете передать нам свои замечания и предложения по содержанию этой и других наших книг через электронную почту по адресам:
frolov@glas.apc.org
frolov.alexandr@usa.net
Если электронная почта вам недоступна, присылайте ваши отзывы в АО “Диалог-МИФИ” по адресу:
115409, Москва, ул. Москворечье, 31, корп. 2,
тел. 324-43-77
Приносим свои извинения за то что не можем ответить на каждое письмо. Мы также не занимаемся продажей и рассылкой книг, дискет, рекламы, отдельных фрагментов наших книг и исходных текстов к книгам. По этим вопросам обращайтесь непосредственно в издательство “Диалог-МИФИ”.
Класс BufferedInputStream
Буферизация операций ввода и вывода в большинстве случаев значительно ускоряет работу приложений, так как при ее использовании сокращается количество обращений к системе для обмена данными с внешними устройствами.
Класс BufferedInputStream может быть использован приложениями Java для организации буферизованных потоков ввода. Заметим, что конструкторы этого класса в качестве параметра получают ссылку на объект класса InputStream. Таким образом, вы не можете просто создать объект класса BufferedInputStream, не создав перед этим объекта класса InputStream. Подробности мы обсудим позже.
Класс BufferedOutputStream
Класс BufferedOutputStream предназначен для создания буферизованных потоков вывода. Как мы уже говорили, буферизация ускоряет работу приложений с потоками.
Класс ByteArrayInputStream
При необходимости вы можете создать в приложениях Java входной поток данных не на базе локального или удаленного файла, а на базе массива, расположенного в оперативной памяти. Класс ByteArrayInputStream предназначен именно для этого - вы передаете конструктору класса ссылку на массив, и получаете входной поток данных, связанный с этим массивом.
Потоки в оперативной памяти могут быть использованы для временного хранения данных. Заметим, что так как аплеты Java не могут обращаться к локальным файлам, для создания временных файлов можно использовать потоки в оперативной памяти на базе класса ByteArrayInputStream. Другую возможность предоставляет класс StringBufferInputStream, рассмотренный ниже.
С помощью класса ByteArrayInputStream вы можете создать входной поток на базе массива байт, расположенного в оперативной памяти. В этом классе определено два конструктора:
public ByteArrayInputStream(byte buf[]);
public ByteArrayInputStream(byte buf[], int offset,
int length);
Первый конструктор получает через единственный параметр ссылку на массив, который будет использован для создания входного потока. Второй позволяет дополнительно указать смещение offset и размер области памяти length, которая будет использована для создания потока.
Вот несколько методов, определенных в классе ByteArrayInputStream:
public int available();
public int read();
public int read(byte b[], int off, int len);
public void reset();
public long skip(long n);
Наиболее интересен из них метод available, с помощью которого можно определить, сколько байт имеется во входном потоке для чтения.
Обычно класс ByteArrayInputStream используется вместе с классом DataInputStream, что позволяет организовать форматный ввод данных.
Класс ByteArrayOutputStream
С помощью класса ByteArrayOutputStream можно создать поток вывода в оперативной памяти.
Класс ByteArrayOutputStream создан на базе класса OutputStream. В нем имеется два конструктора, прототипы которых представлены ниже:
public ByteArrayOutputStream();
public ByteArrayOutputStream(int size);
Первый из этих конструкторов создает выходной поток в оперативной памяти с начальным размером буфера, равным 32 байта. Второй позволяет указать необходимый размер буфера.
В классе ByteArrayOutputStream определено несколько достаточно полезных методов. Вот некоторые из них:
public void reset();
public int size();
public byte[] toByteArray();
public void writeTo(OutputStream out);
Метод reset сбрасывает счетчик байт, записанных в выходной поток. Если данные, записанные в поток вам больше не нужны, вы можете вызвать этот метод и использовать память, выделенную для потока, для записи других данных.
С помощью метода size можно определить количество байт данных, записанных в поток.
Метод toByteArray позволяет скопировать данные, записанные в поток, в массив байт. Этот метод возвращает адрес созданного для этой цели массива.
С помощью метода writeTo вы можете скопировать содержимое данного потока в другой выходной поток, ссылка на который передается методу через параметр.
Для выполнения форматированного вывода в поток, вы должны создать поток на базе класса DataOutputStream, передав соответствующему конструктору ссылку на поток класса ByteArrayOutputStream.
Класс DatagramPacket
Перед тем как принимать или передавать данные с использованием методов receive и send вы должны подготовить объекты класса DatagramPacket. Метод receive запишет в такой объект принятые данные, а метод send - перешлет данные из объекта класса DatagramPacket узлу, адрес которого указан в пакете.
Подготовка объекта класса DatagramPacket для приема пакетов выполняется с помощью следующего конструктора:
public DatagramPacket(byte ibuf[], int ilength);
Этому конструктору передается ссылка на массив ibuf, в который нужно будет записать данные, и размер этого массива ilength.
Если вам нужно подготовить пакет для передачи, воспользуйтесь конструктором, который дополнительно позволяет задать адрес IP iaddr и номер порта iport адресата:
public DatagramPacket(byte ibuf[], int ilength,
InetAddress iaddr, int iport);
Таким образом, информация о том, в какой узел и на какой порт необходимо доставить пакет данных, хранится не в сокете, а в пакете, то есть в объекте класса DatagramPacket.
Помимо только что описанных конструкторов, в классе DatagramPacket определены четыре метода, позволяющие получить данные и информацию об адресе узла, из которого пришел пакет, или для которого предназначен пакет.
Метод getData возвращает ссылку на массив данных пакета:
public byte[] getData();
Размер пакета, данные из которого хранятся в этом массиве, легко определить с помощью метода getLength:
public int getLength();
Методы getAddress и getPort позволяют определить адрес и номер порта узла, откуда пришел пакет, или узла, для которого предназначен пакет:
public InetAddress getAddress();
public int getPort();
Если вы создаете клиент-серверную систему, в которой сервер имеет заранее известный адрес и номер порта, а клиенты - произвольные адреса и различные номера портов, то после получения пакета от клиента сервер может определить с помощью методов getAddress и getPort адрес клиента для установления с ним связи.
Если же адрес сервера неизвестен, клиент может посылать широковещательные пакеты, указав в объекте класса DatagramPacket адрес сети. Такая методика обычно используется в локальных сетях.
Как указать адрес сети?
Напомним, что адрес IP состоит из двух частей - адреса сети и адреса узла. Для разделения компонент 32-разрядного адреса IP используется 32-разрядная маска, в которой битам адреса сети соответствуют единицы, а битам адреса узла - нули.
Например, адрес узла может быть указан как 193.24.111.2. Исходя из значения старшего байта адреса, это сеть класса С, для которой по умолчанию используется маска 255.255.255.0. Следовательно, адрес сети будет такой: 193.24.111.0. Подробнее об адресации в сетях TCP/IP вы можете прочитать в 23 томе “Библиотеки системного программиста”, о котором мы упоминали в разделе “Передача данных с использованием сокетов” нашей книги.
Класс DatagramSocket
Рассмотрим конструкторы и методы класса DatagramSocket, предназначенного для создания и использования датаграммных сокетов.
В классе DatagramSocket определены два конструктора, прототипы которых представлены ниже:
public DatagramSocket(int port);
public DatagramSocket();
Первый из этих конструкторов позволяет определить порт для сокета, второй предполагает использование любого свободного порта.
Обычно серверные приложения работают с использованием какого-то заранее определенного порта, номер которого известен клиентским приложениям. Поэтому для серверных приложений больше подходит первый из приведенных выше конструкторов.
Клиентские приложения, напротив, часто применяют любые свободные на локальном узле порты, поэтому для них годится конструктор без параметров.
Кстати, с помощью метода getLocalPort приложение всегда может узнать номер порта, закрепленного за данным сокетом:
public int getLocalPort();
Прием и передача данных на датаграммном сокете выполняется с помощью методов receive и send, соответственно:
public void receive(DatagramPacket p);
public void send(DatagramPacket p);
В качестве параметра этим методам передается ссылка на пакет данных (соответственно, принимаемый и передаваемый), определенный как объект класса DatagramPacket. Этот класс будет рассмотрен в следующем разделе нашей книги.
Еще один метод в классе DatagramSocket, которым вы будете пользоваться, это метод close, предназначенный для закрытия сокета:
public void close();
Напомним, что сборка мусора в Java выполняется только для объектов, находящихся в оперативной памяти. Такие объекты, как потоки и сокеты, вы должны закрывать после использования самостоятельно.
Класс DataInputStream
Составляя программы на языке программирования С, вы были вынуждены работать с потоками на уровне байт или, в лучшем случае, на уровне текстовых строк. Однако часто возникает необходимость записывать в потоки данных и читать оттуда объекты других типов, например, целые числа и числа типа double, числа в формате с плавающей десятичной точкой, массивы байт и символов и так далее.
Класс DataInputStream содержит методы, позволяющие извлекать из входного потока данные в перечисленных выше форматах или, как говорят, выполнять форматированный ввод данных. Он также реализует интерфейс DataInput, служащий для этой же цели. Поэтому класс DataInputStream очень удобен и часто применяется в приложениях для работы с потоками ввода.
Так же как и конструктор класса BufferedInputStream, конструктор класса DataInputStream должен получить через свои параметр ссылку на объект класса InputStream.
Класс DataOutputStream
С помощью класса DataOutputStream приложения Java могут выполнять форматированный вывод данных. Для ввода форматированных данных вы должны создать входной поток с использованием класса DataInputStream, о котором мы уже говорили. Класс DataOutputStream реализует интерфейс DataOutput.
Класс DrawEllipse
Исходный текст класса DrawEllipse лишь немного отличается от исходного текста класса DrawRectangles. Отличие есть в методе run - этот метод рисует не прямоугольники, а закрашенные эллипсы, вызывая для этого метод fillOval.
Класс File
Класс File предназначен для работы с оглавлениями каталогов. С помощью этого класса можно получить список файлов и каталогов, расположенных в заданном каталоге, создать или удалить каталог, переименовать файл или каталог, а также выполнить некоторые другие операции.
Класс FileDescriptor
C помощью класса FileDescriptor вы можете проверить идентификатор открытого файла.
Класс FileInputStream
Этот класс позволяет создать поток ввода на базе класса File или FileDescriptor.
Класс FileOutputStream
Этот класс позволяет создать поток вывода на базе класса File или FileDescriptor.
Класс FilterInputStream
Класс FilterInputStream, который происходит непосредственно от класса InputStream, является абстрактным классом, на базе которого созданы классы BufferedInputStream, DataInputStream, LineNumberInputStream и PushBackInputStream. Непосредственно класс FilterInputStream не используется в приложениях Java, так как, во-первых, он является абстрактным и предназначен для переопределения методов базового класса InputStream, а во-вторых, наиболее полезные методы для работы с потоками ввода имеются в классах, созданных на базе класса FilterInputStream.
Класс FilterOutputStream
Абстрактный класс FilterOutputStream служит прослойкой между классом OutputStream и классами BufferedOutputStream, DataOutputStream, а также PrintStream. Он выполняет роль, аналогичную роли рассмотренного ранее класса FilterIntputStream.
Класс фрейма для комбинированного приложения
Теперь о классе CombiFrame.
Определение этого класса выглядит достаточно просто:
class CombiFrame extends Frame
{
public CombiFrame(String str)
{
super (str);
}
public boolean handleEvent(Event evt)
{
switch (evt.id)
{
case Event.WINDOW_DESTROY:
{
dispose();
System.exit(0);
return true;
}
default:
return super.handleEvent(evt);
}
}
}
Класс CombiFrame создан на базе класса Frame и предназначен для создания окна фрейма, в которое будет добавлен аплет. В этом классе определен конструктор и метод handleEvent.
Конструктор выполняет простую задачу - создание окна фрейма. Для этого он вызывает конструктор базового класса Frame, передавая ему через параметр строку заголовка окна.
Задача метода handleEvent - удаление окна фрейма, когда пользователь пытается его закрыть, сделав, например, щелчок мышью по правой кнопке в заголовке окна. В случае такой попытки методу handleEvent передается извещение с кодом Event.WINDOW_DESTROY.
В процессе обработки этого извещения метод handleEvent удаляет окно фрейма, вызывая метод dispose, а затем завершает работу приложения, вызывая статический метод exit из класса System.
Все прочие извещения передаются методу handleEvent, определенному в базовом классе.
Класс Image
Как видите, процесс рисования растрового изображения в окне аплета предельно прост - вам достаточно загрузить изображение методом getImage и затем нарисовать его методом drawImage.
Но не забывайте, что метод getImage в действительности только создает объект класса Image, но не загружает его. Давайте посмотрим на класс Image.
В этом классе имеется единственный конструктор без параметров:
public Image();
Вы, однако, скорее всего будете создавать объекты класса Image при помощи метода getImage.
Методы getHeight и getWidth, определенные в классе Image, позволяют определить, соответственно, высоту и ширину изображения:
public abstract int getHeight(ImageObserver observer);
public abstract int getWidth(ImageObserver observer);
Так как при вызове этих методов изображение еще может быть не загружено, в качестве параметров методам передается ссылка на объект ImageObserver. Этот объект получит извещение, когда будет доступна высота или ширина изображения.
Метод getGraphics позволяет получить так называемый внеэкранный контекст отображения для рисования изображения не в окне аплета, а в оперативной памяти:
public abstract Graphics getGraphics();
Эта техника используется для того, чтобы вначале подготовить изображение в памяти, а затем за один прием отобразить его на экране.
Еще один метод класса Image, который мы рассмотрим, называется flush:
public abstract void flush();
Он освобождает ресурсы, занятые изображением.
Класс InputStream
Класс InputStream является базовым для большого количества классов, на базе которых создаются потоки ввода. Именно эти, производные классы применяются программистами, так как в них имеются намного более мощные методы, чем в классе InputStream. Эти методы позволяют работать с потоком ввода не на уровне отдельных байт, а на уровне объектов различных классов, например, класса String и других.
Класс LineNumberInputStream
С помощью класса LineNumberInputStream вы можете работать с текстовыми потоками, состоящими из отдельных строк, разделенных символами возврата каретки \r и перехода на следующую строку \n. Методы этого класса позволяют следить за нумерацией строк в таких потоках.
Класс OutputStream
Аналогично, класс OutputStream служит в качестве базового для различных классов, имеющих отношение к потокам вывода.
Класс PipedInputStream
С помощью классов PipedInputStream и PipedOutputStream можно организовать двухстороннюю передачу данных между двумя одновременно работающими задачами мультизадачного аплета.
Класс PipedOutputStream
Как мы уже говорили, классы PipedInputStream и PipedOutputStream предназначены для организации двухсторонней передачи данных между двумя одновременно работающими задачами мультизадачного аплета.
Класс PrintStream
Потоки, созданные с использованием класса PrintStream, предназначены для форматного вывода данных различных типов с целью их визуального представления в виде текстовой строки. Аналогичная операция в языке программирования С выполнялась функцией printf.
Класс PushBackInputStream
Класс PushBackInputStream позволяет возвратить в поток ввода только что прочитанный оттуда символ, с тем чтобы после этого данный символ можно было прочитать снова.
Класс RandomAccesFile
С помощью класса RandomAccesFile можно организовать работу с файлами в режиме прямого доступа, когда программа указывает смещение и размер блока данных, над которым выполняется операция ввода или вывода. Заметим, кстати, что классы InputStream и OutputStream также можно использовать для обращения к файлам в режиме прямого доступа.
Класс SequenceInputStream
Класс SequenceInputStream позволяет объединить несколько входных потоков в один поток. Если в процессе чтения будет достигнут конец первого потока такого объединения, в дальнейшем чтение будет выполняться из второго потока и так далее.
Класс SimpleDBMS
Рассмотрим теперь класс SimpleDBMS.
В этом классе определено три поля с именами idx, dat и idxFilePointer, а также три метода.
Класс StreamTokenizer для разбора входных потоков
Если вы создаете приложение, предназначенное для обработки текстов (например, транслятор или просто разборщик файла конфигурации, содержащего значения различных параметров), вам может пригодиться класс StreamTokenizer. Создав объект этого класса для входного потока, вы можете легко решить задачу выделения из этого потока отдельных слов, символов, чисел и строк комментариев.
Класс StreamTokenizer
Очень удобен класс StreamTokenizer. Он позволяет организовать выделение из входного потока данных элементов, отделенных друг от друга заданными разделителями, такими, например, как запятая, пробел, символы возврата каретки и перевода строки.
Класс StringBufferInputStream
Класс StringBufferInputStream позволяет создавать потоки ввода на базе строк класса String, используя при этом только младшие байты хранящихся в такой строке символов. Этот класс может служить дополнением для класса ByteArrayInputStream, который также предназначен для создания потоков на базе данных из оперативной памяти.
Класс StringBufferInputStream предназначен для создания входного потока на базе текстовой строки класса String. Ссылка на эту строку передается конструктору класса StringBufferInputStream через параметр:
public StringBufferInputStream(String s);
В классе StringBufferInputStream определены те же методы, что и в только что рассмотренном классе ByteArrayInputStream. Для более удобной работы вы, вероятно, создадите на базе потока класса StringBufferInputStream поток класса DataInputStream.
Класс StringTokenizer
Рассказывая о классе StreamTokenizer, нельзя не упомянуть о другом классе с похожим названием и назначением, а именно о классе StringTokenizer.
Определение этого класса достаточно компактно, поэтому мы приведем его полностью:
public class java.util.StringTokenizer
extends java.lang.Object
implements java.util.Enumeration
{
// ---------------------------------------------------
// Конструкторы класса
// ---------------------------------------------------
public StringTokenizer(String str);
public StringTokenizer(String str, String delim);
public StringTokenizer(String str, String delim,
boolean returnTokens);
// ---------------------------------------------------
// Методы
// ---------------------------------------------------
public String nextToken();
public String nextToken(String delim);
public int countTokens();
public boolean hasMoreElements();
public boolean hasMoreTokens();
public Object nextElement();
}
Класс StringTokenizer не имеет никакого отношения к потокам, так как предназначен для выделения отдельных элементов из строк типа String.
Конструкторы класса получают в качетсве первого параметра str ссылку на разбираемую строку. Второй параметр delim, если он есть, задает разделители, с испльзованием которых в строке будут выделяться элементы. Параметр returnTokens определяет, надо ли вовзвращать обнаруженные разделители как элементы разбираемой строки.
Рассмотрим кратко методы класса StringTokenizer.
Для разбора строки приложение должно организовать цикл, вызывая в нем метод nextToken. Условием завершения цикла может быть либо возникновение исключения NoSuchElementException, либо возврат значения false методами hasMoreElements или hasMoreTokens.
Метод countTokens позволяет определить, сколько раз был вызван метод nextToken перед возникновением исключения NoSuchElementException.
Класс URL в библиотеке классов Java
Для работы с ресурсами, заданными своими адресами URL, в библиотеке классов Java имеется очень удобный и мощный класс с названием URL. Простота создания сетевых приложений с использованием этого класса в значительной степени опровергает общераспространенное убеждение в сложности сетевого программирования. Инкапсулируя в себе достаточно сложные процедуры, класс URL предоставляет в распоряжение программиста небольшой набор простых в использовании конструкторов и методов.
Класс URLConnection
Напомним, что в классе URL, рассмотренном нами в начале этой главы, мы привели прототип метода openConnection, возвращающий для заданного объекта класса URL ссылку на объект URLConnection:
public URLConnection openConnection();
Что мы можем получить, имея ссылку на этот объект?
Прежде всего, пользуясь этой ссылкой, мы можем получить содержимое объекта, адресуемое соответствующим объектом URL, методом getContent:
public Object getContent();
Заметим, что метод с таким же названием есть и в классе URL. Поэтому если все, что вы хотите сделать, это получение содержимое файла, адресуемое объектом класса URL, то нет никакой необходимости обращаться к классу URLConnection.
Метод getInputStream позволяет открыть входной поток данных, с помощью которого можно считать файл или получить данные от расширения сервера Web:
public InputStream getInputStream();
В классе URLConnection определен также метод getOutputStream, позволяющий открыть выходной поток данных:
public OutputStream getOutputStream();
Не следует думать, что этот поток можно использовать для записи файлов в каталоги сервера Web. Однако для этого потока есть лучшее применение - с его помощью можно передать данные расширению сервера Web.
Рассмотрим еще несколько полезных методов, определенных в классе URLConnection.
Метод connect предназначен для установки соединения с объектом, на который ссылается объект класса URL:
public abstract void connect();
Перед установкой соединения приложение может установить различные параметры соединения. Некоторые из методов, предназначенных для этого, приведены ниже:
// Включение или отключение кэширования по умолчанию
public void
setDefaultUseCaches(boolean defaultusecaches);
// Включение или отключение кэширования
public void setUseCaches(boolean usecaches);
// Возможность использования потока для ввода
public void setDoInput(boolean doinput);
// Возможность использования потока для вывода
public void setDoOutput(boolean dooutput);
// Установка даты модификации документа
public void setIfModifiedSince(long ifmodifiedsince);
В классе URLConnection есть методы, позволяющие определить значения параметров, установленных только что описанными методами:
public boolean getDefaultUseCaches();
public boolean getUseCaches();
public boolean getDoInput();
public boolean getDoOutput();
public long getIfModifiedSince();
Определенный интерес могут представлять методы, предназначенные для извлечения информации из заголовка протокола HTTP:
// Метод возвращает содержимое заголовка content-encoding
// (кодировка ресурса, на который ссылается URL)
public String getContentEncoding();
// Метод возвращает содержимое заголовка content-length
// (размер документа)
public int getContentLength();
// Метод возвращает содержимое заголовка content-type
// (тип содержимого)
public String getContentType();
// Метод возвращает содержимое заголовка date
// (дата посылки ресурса в секундах с 1 января 1970 года)
public long getDate();
// Метод возвращает содержимое заголовка last-modified
// (дата изменения ресурса в секундах с 1 января 1970 года)
public long getLastModified();
// Метод возвращает содержимое заголовка expires
// (дата устаревания ресурса в секундах с
// 1 января 1970 года)
public long getExpiration();
Другие методы, определенные в классе URLConnection, позволяют получить все заголовки или заголовки с заданным номером, а также другую информацию о соединении. Мы не будем их рассматривать для экономии места в книге. При необходимости вы найдете описание этих методов в справочной системе Microsoft Visual J++.
Классы Java для работы с потоками
Программист, создающий автономное приложение Java, может работать с потоками нескольких типов:стандартные потоки ввода и вывода;
потоки, связанные с локальными файлами;
потоки, связанные с файлами в оперативной памяти;
потоки, связанные с удаленными файлами
Рассмотрим кратко классы, связанные с потоками.
Конструктор класса DrawRectangles
В качестве параметра конструктору передается ссылка на класс аплета. Конструктор использует эту ссылку для получения и сохранения в полях класса контекста отображения и размеров окна аплета:
g = Appl.getGraphics();
dimAppWndDimension = Appl.size();
Для получения контекста отображения окна аплета, ссылка на который передается через единственный параметр, конструктор класса DrawRectangles вызывает метод getGraphics:
public DrawRectangles(Applet Appl)
{
g = Appl.getGraphics();
dimAppWndDimension = Appl.size();
}
Размеры окна аплета определяются с помощью метода size.
Конструктор класса SimpleDBMS
Конструктор класса SimpleDBMS выглядит достаточно просто. Все, что он делает, - это создает два объекта класса RandomAccessFile, соответственно, для индекса и данных:
idx = new RandomAccessFile(IndexFile, "rw");
dat = new RandomAccessFile(DataFile, "rw");
Так как в качестве второго параметра конструктору класа RandomAccessFile передается строка "rw", файлы открываются и для чтения, и для записи.
Конструктор класса StreamTokenizer
Для создание объектов класса StreamTokenizer предусмотрен всего один конструктор:
public StreamTokenizer(InputStream istream);
В качестве параметра этому конструктору необходимо передать ссылку на заранее созданный входной поток.
Конструкторы и методы класса Socket
После краткого введения в сокеты приведем описание наиболее интересных конструкторов и методов класса Socket.
Конструкторы класса Socket
Чаще всего для создания сокетов в клиентских приложениях вы будете использовать один из двух конструкторов, прототипы которых приведены ниже:
public Socket(String host, int port);
public Socket(InetAddress address, int port);
Первый из этих конструкторов позволяет указывать адрес серверного узла в виде текстовой строки, второй - в виде ссылки на объект класса InetAddress. Вторым параметром задается номер порта, с использованием которого будут передаваться данные.
В классе Socket определена еще одна пара конструкторов, которая, однако не рекомендуется для использования:
public Socket(String host, int port, boolean stream);
public Socket(InetAddress address, int port,
boolean stream);
В этих конструкторах последний параметр определяет тип сокета. Если этот параметр равен true, создается потоковый сокет, а если false - датаграммный. Заметим, однако, что для работы с датаграммными сокетами следует использовать класс DatagramSocket.
Конструкторы класса URL
Сначала о конструкторах. Их в классе URL имеется четыре штуки.
public URL(String spec);
Первый из них создает объект URL для сетевого ресурса, адрес URL которого передается конструктору в виде текстовой строки через единственный параметр spec:
public URL(String spec);
В процессе создания объекта проверяется заданный адрес URL, а также наличие указанного в нем ресурса. Если адрес указан неверно или заданный в нем ресурс отсутствует, возникает исключение MalformedURLException. Это же исключение возникает при попытке использовать протокол, с которым данная система не может работать.
Второй вариант конструктора класса URL допускает раздельное указание протокола, адреса узла, номера порта, а также имя файла:
public URL(String protocol, String host, int port,
String file);
Третий вариант предполагает использование номера порта, принятого по умолчанию:
public URL(String protocol, String host, String file);
Для протокола HTTP это порт с номером 80.
И, наконец, четвертый вариант конструктора допускает указание контекста адреса URL и строки адреса URL:
public URL(URL context, String spec);
Строка контекста позволяет указывать компоненты адреса URL, отсустсвующие в строке spec, такие как протокол, имя узла, файла или номер порта.
Литература
1. Фролов А.В., Фролов Г.В. Библиотека системного программиста. М.: ДИАЛОГ-МИФИ
Т.11 - 13. Операционная система Microsoft Windows 3.1 для программиста, 1994
Т.14. Графический интерфейс GDI в Microsoft Windows, 1994
Т.15. Мультимедиа для Windows, 1994
Т.22. Операционная система Windows 95 для программиста, 1996
Т.23. Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets
Т.29. Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI, установка серверов Web для Windows, 1997
Т.30. Microsoft Visual J++. Создание приложений на языке Java. Часть 1, 1997
2. Д. Родли, Создание Java-апплетов: Пер. с англ., К: НИПФ “ДиаСофт Лтд.”, 1996
3. S. Davic, Learn Lava Now, Microsoft Press, One Microsoft Way, 1996
4. К. Джамса, Java: Пер. с англ., Мн.: ООО “Поппури”, 1996
5. Баженова И.Ю.,Язык программирования Java, М.: ДИАЛОГ-МИФИ, 1997
Метод action
Метод action получает управление, когда пользователь нажимает на одну из кнопок, расположенных в окне аплета. В зависимости от того, какая именно кнопка была нажата, выполняются различные действия.
Если пользователь нажал кнопку Play, вызывается метод play для запуска однократного проигрывания звукового файла:
auClip.play();
btStop.enable();
Сразу после того как проигрывание будет запущено, приложение разблокирует кнопку Stop, предоставляя пользователю возможность прервать звучание.
В том случае, когда пользователь нажал кнопку Loop, вызывается метод loop, запусчкающий проигрывание звукового файла в цикле:
auClip.loop();
fLoopPlay = true;
btStop.enable();
После запуска устанавливается флаг fLoopPlay и разблокируется кнопка Stop.
И, наконец, если пользователь нажимает кнопку Stop, выполняется остановка проигрывания методом stop:
auClip.stop();
fLoopPlay = false;
btStop.disable();
Флаг fLoopPlay сбрасывается, после чего кнопка Stop блокируется.
Метод action обрабатывает события, вызванные нажатием кнопок в окне аплета Inspector. Обработка заключается в вызове сооветствующего метода с использованием ссылки на аплет Audio. Например, если пользователь нажал кнопку Play, метод action вызывает метод play:
if(evt.target.equals(btPlay))
{
appAudio.auClip.play();
btStop.enable();
}
Обратите внимание, что здесь мы ссылаемся через поле appAudio на поле auClip, определенное в аплете Audio.
Метод AddRecord
Метод AddRecord добавляет новую запись в конец файла данных, а смещение этой записи - в конец файла индекса. Поэтому перед началом своей работы текущая позиция обоих указанных файлов устанавливается на конец файла.
Для установки мы применили метод seek из класса RandomAccessFile, передав ему в качестве параметра значение длины файла в байтах, определенное при помощи метода length из того же класса:
idx.seek(idx.length());
dat.seek(dat.length());
Перед тем как добавлять новую запись в файл данных, метод AddRecord определяет текущую позицию в файле данных (в данном случае это позиция конца файла) и записывает эту позицию в файл индекса:
idxFilePointer = dat.getFilePointer();
idx.writeLong(idxFilePointer);
Далее метод AddRecord выполняет сохранение полей записи в файле данных. Для записи строки вызывается метод writeBytes, а для записи численного значения типа int - метод writeInt:
dat.writeBytes(name + "\r\n");
dat.writeInt(account);
Обратите внимение, что к строке мы добавляем символы возврата каретки и перевода строки. Это сделано исключительно для того чтобы обозначить конец строки текстового поля.
Метод close
Метод close закрывает файлы индекса и данных, вызывая метод close из класса RandomAccessFile:
idx.close();
dat.close();
Метод destroy
При завершении работы аплета управление передается методу destroy. Мы его не используем.
Метод displayImage
Метод displayImage вызывается из двух мест - из метода paint при перерисовке окна аплета и из метода run (периодически).
Если кадры видеофильма не загружены, содержимое флага m_fAllLoaded равно false и метод displayImage просто возвращает управление, ничего не делая:
if(!m_fAllLoaded)
return;
Если же загрузка изображений завершена, этот метод рисует в центре окна текущий кадр видеофильма, вызывая для этого знакомый вам метод drawImage:
g.drawImage(m_Images[m_nCurrImage],
(size().width - m_nImgWidth) / 2,
(size().height - m_nImgHeight) / 2, null);
После того как кадр нарисован, мы надписываем на нем его порядковый номер, вызывая для этого метод drawString:
g.drawString((new Integer(m_nCurrImage)).toString(),
(size().width - m_nImgWidth) / 2,
((size().height - m_nImgHeight) / 2) + 10);
Метод equals
Вы можете использовать метод equals для определения идентичности адресов URL, заданных двумя объектами класса URL:
public boolean equals(Object obj);
Если адреса URL идентичны, метод equals возвращает значение true, если нет - значение false.
Метод getAppletInfo
Метод getAppletInfo возвращает информацию об аплете.
Метод getAppletInfo предоставляет другим заинтересованным аплетам (и себе, в частности) строку информации об аплете Inspector. Эта строка аналогична строкам, которые возвращают другие наши аплеты.
Метод getContent
Очень интересен метод getConten. Этот метод определяет и получает содержимое сетевого ресурса, для которого создан объект URL:
public final Object getContent();
Практически вы можете использовать метод getContent для получения текстовых файлов, расположенных в сетевых каталогах.
К сожалению, данный метод непригоден для получения документов HTML, так как для данного ресурса не определен обработчик соедржимого, предназначенный для создания объекта. Метод getContent не способен создать объект ни из чего другого, кроме текстового файла.
Данная проблема, тем не менее, решается очень просто - достаточно вместо метода getContent использовать описанную выше комбинацию методов openStream из класса URL и read из класса InputStream.
Метод getFile
Метод getFile позволяет получить информацию о файле, связанном с данным объектом URL:
public String getFile();
Метод getHost
С помощью метода getHost вы можете определить имя узла, соответствующего данному объекту URL:
public String getHost();
Метод getParameterInfo
Метод getParameterInfo возвращает описание единственного параметра нашего аплета, через который передается имя звукового файла.
Метод getPort
Метод getPort предназначен для определения номера порта, на котором выполняется связь для объекта URL:
public int getPort();
Метод getProtocol
С помощью метода getProtocol вы можете определить протокол, с использованием которого установлено соединение с ресурсом, заданным объектом URL:
public String getProtocol();
Метод GetRecordByNumber
Метод GetRecordByNumber позволяет извлечь произвольную запись из файла данных по ее порядковому номеру.
Напомним, что смещения всех записей хранятся в файле индексов и имеют одинаковую длину 8 байт. Пользуясь этим, метод GetRecordByNumber вычисляет смещение в файле индекса простым умножением порядкового номера записи на длину переменной типа long, то есть на 8 байт, а затем выполняет позиционирование:
idx.seek(nRec * 8);
После этого метод GetRecordByNumber извлекает из файла индексов смещение нужной записи в файле данных, вызывая для этого метод readLong, а затем выполняет позиционирование в файле данных:
idxFilePointer = idx.readLong();
dat.seek(idxFilePointer);
Поля записи читаются из файла данных в два приема. Вначале читается строка текстового поля, а затем - численное значение, для чего вызываются, соответственно, методы readLine и readInt:
str = dat.readLine();
account = new Integer(dat.readInt());
Полученные значения полей объединяются в текстовой строке и записываются в переменную sRecord:
sRecord = new String("> " + account + ", " + str);
Содержимое этой переменной метод GetRecordByNumber возвращает в качестве извлеченной строки записи базы данных.
Метод getRef
Метод getRef возвращает текстовую строку ссылки на ресурс, соответствующий данному объекту URL:
public String getRef();
Метод hashCode
Метод hashCode возвращает хэш-код объекта URL:
public int hashCode();
Метод imageUpdate
Метод imageUpdate периодически вызывается в процессе загрузки изображения, конструируя каждый раз значение флага полной загрузки fAllLoaded следующим образом:
fAllLoaded = ((flags & ALLBITS) != 0);
Когд изображение будет полностью загружено, в параметре flags метода imageUpdate будет установлен флаг ALLBITS, после чего флаг fAllLoaded будет установлен в значение true.
Как только это произойдет, метод imageUpdate вызовет метод repaint, выполнив принудительную перерисовку окна аплета:
if(fAllLoaded)
repaint();
При этом метод paint нарисует в окне аплета изображение фона, закрасив им сообщение о ходе процесса загрузки изображения.
Метод imageUpdate должен возвратить значение false или true. Если изображение еще не загружено, возвращается значение true:
return !fAllLoaded;
При этом метод imageUpdate будет вызываться еще раз для отслеживания процесса загрузки.
Когда загрузка будет завершена, метод imageUpdate возвратит значение false, после чего этот метод вызываться больше не будет.
Метод init
Метод init вызывается один раз при инициализации аплета. Наше приложение его не использует.
Метод init
Единственная задача метода init нашего приложения - получение параметров аплета и запись их в соотвестствующие поля класса. Эта задача решается с помощью метода getParameter, при этом строка param типа String используется как рабочая:
String param;
param = getParameter(PARAM_String1);
if (param != null)
m_String1 = param;
Аналогичным образом метод получает значения всех шести параметров.
Метод init
Метод init получает текущие значения параметров аплета и сохраняет их в соответствующих полях основного класса. Способ получения параметров аналогичен использованному в предыдущем приложении.
Метод init
В начале своей работы метод init записывает в поле sOut текстовую строку, которая будет записана в выходной поток:
String sOut;
sOut = "Hello, Java!";
Далее метод init создает поток baStream класса ByteArrayOutputStream, устанавливая начальный размер выделенного для него массива равным 255 байт:
ByteArrayOutputStream baStream =
new ByteArrayOutputStream(255);
Для выполнения форматированного вывода нам нужен поток класса DataOutputStream, который мы и создаем на базе потока baStream:
OutStream = new DataOutputStream(
new BufferedOutputStream(baStream));
Для записи строки в выходной поток мы воспользовались методом writeBytes:
OutStream.writeBytes(sOut);
Так как наш выходной поток буферизован, после вызова метода writeBytes данные могут остаться в промежуточном буфере, не достигнув массива, выделенного для хранения потока. Чтобы переписать данные из буфера в массив, мы выполняем сброс буфера методом flush:
OutStream.flush();
После сброса буфера (и только после этого) можно копировать содержимое потока методом toByteArray:
bMemStream = baStream.toByteArray();
Этот метод возвращает ссылку на созданный массив, которую мы записываем в поле bMemStream. В дальнейшем на базе этого массива мы создадим поток ввода.
Перед завершением своей работы метод init закрывает входной поток,вызывая метод close:
OutStream.close();
Метод init
Во время инициализации метод init создает объект класса URL для файла исходных данных:
SrcURL = new URL("http://frolov/chart.txt");
Здесь для экономии места в книге мы указали адрес URL файла исходных данных непосредственно в программе, однако вы можете передать этот адрес аплету через параметр в документе HTML.
Далее для нашего объекта URL мы создаем канал и получаем содержимое объекта (то есть исходные данные для построения диаграммы):
URLContent = SrcURL.openConnection().getContent();
Здесь использована двухступенчатая процедура получения содержимого с созданием канала как объекта класса URLConnection. Вы также можете упростить этот код, воспользовавшись методом getContent из класса URL:
URLContent = SrcURL.getContent();
Результат в обоих случаях будет одинаковый - содержимое файла исходных данных окажется записанным в поле URLContent класса Object.
Если при создании объекта класса URL возникло исключение, метод init записывает в поле errno код ошибки, равный 2, записывая при этом в строку состояния навигатора сообщение “MalformedURLException exception”.
В том случае, когда объект класса URL создан успешно, а исключение возникло в процессе получения содержимого файла, в поле errno записывается значение 1, а в строку состояния навигатора - сообщение "getContent exception".
Метод init
Метод init создает два объекта класса Image для файлов disk.gif и cd.gif:
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
CDDiskImg = getImage(getCodeBase(), "cd.gif");
В качестве первого параметра методу getImage передается адрес URL аплета, полученный при помощи метода getCodeBase. При этом предполагается, что файлы disk.gif и cd.gif находятся в том же каталоге, что и аплет.
Метод init
Прежде всего метод init создает объект класса MediaTracker, который будет использоваться для отслеживания процесса загрузки изображений:
mt = new MediaTracker(this);
Далее метод init последовательно создает три объекта класса Image (соответственно, для изображений фона, для флоппи-диска и для компакт-диска), а затем добавляет их в объект MediaTracker с помощью метода addImage:
BkgImg = getImage(getCodeBase(), "bkg.gif");
mt.addImage(BkgImg , 0);
FloppyDiskImg = getImage(getCodeBase(), "disk.gif");
mt.addImage(FloppyDiskImg, 0);
CDDiskImg = getImage(getCodeBase(), "cd.gif");
mt.addImage(CDDiskImg, 0);
Метод init
В процессе инициализации аплета метод init создает объект класса Image, соответствующий изображению фона:
public void init()
{
BkgImg = getImage(getCodeBase(), "bkg.gif");
}
Как вы уже знаете, при этом реальная загрузка файла изображения не выполняется.
Метод init
Сразу после запуска аплета метод init получает значение параметра - имя звукового файла, и если этот параметр задан в документе HTML, записывает полученное имя в поле m_ClipName:
param = getParameter(PARAM_ClipName);
if(param != null)
m_ClipName = param;
Далее создаются три кнопки, управляющие звучанием аплета:
btPlay = new Button("Play");
btLoop = new Button("Loop");
btStop = new Button("Stop");
Кнопка Stop блокируется, так как на данный момент проигрывание еще не запущено:
btStop.disable();
Для блокирования вызывается метод disable, определенный в классе Button.
Подготовленные таким образом кнопки добавляются в окно аплета:
add(btPlay);
add(btLoop);
add(btStop);
Напомним, что работа с кнопками и другими органами управления в приложениях Java была нами описана в 30 томе “Библиотеки системного программиста”, который называется “Microsoft Visual J++. Создание приложений на языке Java. Часть 1”.
Последнее, что делает метод init перед тем как возвратить управление, это получение ссылки на интерфейс AudioClip:
auClip = Applet.getAudioClip(getCodeBase(),m_ClipName);
Адрес URL каталога, в котором расположен аплет, определяется с помощью метода getCodeBase, о котором мы говорили в предыдущей главе.
Метод init
Метод init создает три кнопки, предназначенные для управления аплетом Audio, причем кнопка с названием Stop блокируется:
btPlay = new Button("Play");
btLoop = new Button("Loop");
btStop = new Button("Stop");
btStop.disable();
Далее созданные кнопки добавляются в окно аплета Inspector.
add(btPlay);
add(btLoop);
add(btStop);
Метод init
Метод init вызывается независимо от режима, в котором работает приложение - автономно или как аплет.
После изменения размеров окна аплета методом resize метод init определяет некоторые параметры среды выполнения приложения. Для этого используется метод getProperty, определенный в классе System. Подробное описание этого метода выходит за рамки нашей книги. Однако мы отметим, что передавая этому методу в виде текстовой строки названия параметров среды выполнения, можно получить в текстовом виде значения этих параметров.
Наш метод init использует метод getProperty для определения названия операционной системы, под управлением которой работает приложение, передавая ему строку "os.name":
String str = System.getProperty("os.name");
Далее метод init проверяет, есть ли в строке названия операционной системы слово Windows, и если есть, пытается запустить программу калькулятора:
if(str.indexOf("Windows") != -1)
{
Runtime rt = Runtime.getRuntime();
try
{
rt.exec("calc.exe");
}
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
}
Калькулятор запускается с помощью класса Runtime, который предоставляет системно-зависимые методы (именно поэтому перед его использованием мы проверили название операционной системы).
Ссылка на объект класса Runtime должна быть получена с помощью статического метода getRuntime. Получив такую ссылку, мы запускаем программу калькулятора с помощью метода exec, который определен в реализации класса Runtime для операционных систем Microsoft Windows 95 и Microsoft Windows NT.
Заметим, что одно из важнейших достоинств приложений Java заключается в возможности достижения независимости от платформ, на которых они выполняются. Поэтому использование класса Runtime оправдано только тех случаях, когда приложение Java разрабатывается специально для конкретной платформы.
Метод main
Сразу после запуска метод main приложения DirectFileAccess создает базу данных, передавая конструктору имена файла индекса dbtest.idx и файла данных dbtest.dat:
SimpleDBMS db = new SimpleDBMS("dbtest.idx", "dbtest.dat");
После этого с помощью метода AddRecord, определенного в классе SimpleDBMS, в базу добавляются три записи, состоящие из текстового и числового полей:
db.AddRecord("Ivanov", 1000);
db.AddRecord("Petrov", 2000);
db.AddRecord("Sidoroff", 3000);
Сразу после добавления записей приложение извлекает три записи с номерами 2, 1 и 0, вызывая для этого метод GetRecordByNumber, также определенный в классе SimpleDBMS:
System.out.println(db.GetRecordByNumber(2));
System.out.println(db.GetRecordByNumber(1));
System.out.println(db.GetRecordByNumber(0));
Извлеченные записи отображаются на системной консоли.
После завершения работы с базой данных она закрывается методом close из класса SimpleDBMS:
db.close();
Метод mouseEnter
В предыдущем томе “Библиотеки системного программиста” мы рассказывали о методах mouseEnter и mouseExit. Первый из этих методов вызывается, когда в результате перемещения курсор мыши оказывается над окном аплета, а второе - когда курсор покидает окно аплета. Мы переопределили эти методы в своем аплете.
Когда курсор мыши оказывается над окном аплета, мы временно приостанавливаем работу задачи, вызывая метод suspend:
public boolean mouseEnter(Event evt, int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.suspend();
}
return true;
}
Преостановленная задача не уничтожается. Ее работа может быть продолжена с помощью метода resume.
Метод mouseExit
Когда курсор мыши покидает окно аплета, вызывается метод mouseExit. Этот метод в нашем аплете возобновляет работу задачи, временно приостановленной методом suspend. Для этого используется метод resume, как это показано ниже:
public boolean mouseExit(Event evt, int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.resume();
}
return true;
}
Метод openConnection
Метод openConnection предназначен для создания канала между приложением и сетевым ресурсом, представленным объектом класса URL:
public URLConnection openConnection();
Если вы создаете приложение, которое позволяет читать из каталогов сервера Web текстовые или двоичные файлы, можно создать поток методом openStream или получить содержимое текстового ресурса методом getContent.
Однако есть и другая возможность. Вначале вы можете создать канал, как объект класса URLConnection, вызвав метод openConnection, а затем создать для этого канала входной поток, воспользовавшись методом getInputStream, определенным в классе URLConnection. Такая методика позволяет определить или установить перед созданием потока некоторые характеристики канала, например, задать кэширование.
Однако самая интересная возможность, которую предоставляет этот метод, заключается в организации взаимодействия приложения Java и сервера Web.
Подробнее методика организации такого взаимодействия и класс URLConnection будет рассмотрен позже.
Метод openStream
Метод openStream позволяет создать входной поток для чтения файла ресурса, связанного с созданным объектом класса URL:
public final InputStream openStream();
Для выполнения операции чтения из созданного таким образом потока вы можете использовать метод read, определенный в классе InputStream (любую из его разновидностей).
Данную пару методов (openStream из класса URL и read из класса InputStream) можно применить для решения задачи получения содержимого двоичного или текстового файла, хранящегося в одном из каталогов сервера Web. Сделав это, обычное приложение Java или аплет может выполнить локальную обработку полученного файла на компьютере удаленного пользователя.
Метод paint
Метод paint рисует в окне аплета текстовую строку и случайное число, полученное при помощи статического метода random класса Math:
public void paint(Graphics g)
{
g.drawString("Running: " + Math.random(), 10, 20);
}
Напомним, что в однозадачном приложении метод paint вызывается при первом создании окна аплета, а также в случае необходимости перерисовки этого окна.
В нашем аплете будет создана отдельная задача, выполняющая периодическую перерисовку окна при помощи метода repaint. Поэтому случайное число в окне аплета будет постоянно меняться.
Метод paint
В предыдущем приложении метод paint периодически получал управление в результате периодического вызова метода repaint, выполняемого отдельной задачей. Метод paint аплета Rectangles вызывается только при инициализации и тогда, когда нужно обновить окно аплета. Этот метод определяет текущие размеры окна аплета, закрашивает окно желтым цветом и рисует вокруг окна черную рамку.
Метод paint
Метод paint подготавливает окно аплета для рисования - закрашивает его в желтый цвет и рисует вокруг окна черную рамку.
Метод paint
Метод paint просто закрашивает окно аплета желтым цветом и затем обводит его черной рамкой.
Метод paint
После традиционного для наших аплетов раскрашивания окна и рисования рамки метод paint создает входной буферизованный поток на базе массива bMemStream:
InStream = new DataInputStream(
new BufferedInputStream(
new ByteArrayInputStream(bMemStream)));
Поток создается в три этапа с помощью классов ByteArrayInputStream, BufferedInputStream и DataInputStream.
Далее мы читаем из созданного таким образом входного потока одну строку, вызывая для этого метод readLine:
g.drawString(InStream.readLine(), 10, 20);
Прочитанная строка отображается в окне аплета методом drawString.
После завершения работы с потоком мы его закрываем, для чего вызываем метод close:
InStream.close();
Метод paint
После раскрашивания фона окна аплета и рисования вокруг него рамки метод paint приступает к рисованию круговой диаграммы.
Прежде всего метод проверяет, является ли полученный из сети объект текстовой строкой класса String. Если является, то выполняется явное преобразование типа:
if(URLContent instanceof String)
{
sChart = (String)URLContent;
}
В случае успеха в переменной sChart будет находиться строка исходных данных для построения диаграммы, а при ошибке - строка “”, записанная туда при инициализации. Кроме того, в поле errno записывается значение 3.
Далее метод paint проверяет, были ли ошибки при создании объекта URL, получении содержимого файла исходных данных или преобразования данных в строку класса String. Если были, то в строку состояния навигатора записывается код ошибки и содержимое строки sChart. Если же ошибок не было, то в строке состояния отображаются исходные данные:
if(errno != 0)
showStatus("errno: " + errno + ", sChart: " + sChart);
else
showStatus(sChart);
На следующем этапе обработчик paint приступает к построению диаграммы.
Первым делом создается разборщик строки исходных данных:
StringTokenizer st =
new StringTokenizer(sChart, ",\r\n");
В качестве разделителей для этого разборщика указывается запятая, символ возврата каретки и перевода строки.
Рисование секторов диаграммы выполняется в цикле, условием выхода из которого является завершение разбора строки исходных данных:
while(st.hasMoreElements())
{
. . .
}
Для того чтобы секторы диаграммы не сливались, они должны иметь разный цвет. Цвет сектора можно было бы передавать вместе со значением угла через файл исходных данных, однако мы применили более простой способ раскаршивания секторов - в случайные цвета. Мы получаем случайные компоненты цвета сектора, а затем выбираем цвет в контекст отображения:
rColor = (int)(255 * Math.random());
gColor = (int)(255 * Math.random());
bColor = (int)(255 * Math.random());
g.setColor(new Color(rColor, gColor, bColor));
Метод paint
После раскрашивания окна аплета в белый цвет и рисования вокруг окна черной рамки метод paint вызывает четыре раза метод drawImage, рисуя изображения флоппи-диска и компакт-диска:
g.drawImage(FloppyDiskImg, 25, 42, 200, 200, this);
g.drawImage(FloppyDiskImg, 25, 3, this);
g.drawImage(CDDiskImg , 70, 3, this);
g.drawImage(CDDiskImg , 115, 3, 40, 25, this);
В первый раз флоппи-диск рисуется с масштабированием, во второй раз - в исходном виде. Компакт-диск вначале рисуется в исходном виде, а затем - растянутым по горизонтали.
Метод paint
Метод paint прежде всего раскрашивает окно аплета в белый цвет и обводит его черной рамкой. Затем на подготовленной таким образом поверхности он пишет сообщение о начале процесса загрузки изображений:
g.drawString("Подождите, идет загрузка...",
20, dimAppWndDimension.height / 2);
Далее ожидается загрузка всех изображений, для чего вызывается метод waitForAll из класса MediaTracker:
try
{
mt.waitForAll();
}
catch (InterruptedException ex)
{
}
Когда все изображения будут загружены, следует серия вызовов метода drawImage, с помощью которых рисуется изображение фона, два изображения флоппи-диска и два изображения компакт-диска:
g.drawImage(BkgImg, 1, 1,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 2, this);
g.drawImage(FloppyDiskImg, 25, 42, 200, 200, this);
g.drawImage(FloppyDiskImg, 25, 3, this);
g.drawImage(CDDiskImg , 70, 3, this);
g.drawImage(CDDiskImg , 115, 3, 40, 25, this);
Метод paint
Свою работу метод paint начинает с раскрашивания окна аплета и рисования рамки вокруг окна. Затем метод проверяет флаг fAllLoaded, начальное значение которого равно false:
if(fAllLoaded == false)
{
g.drawString("Подождите, идет загрузка...",
20, dimAppWndDimension.height / 2);
}
Флаг fAllLoaded служит индикатором полной загрузки изображения и устанавливается методом imageUpdate, отслеживающим загрузку. Пока значение этого флага равно false, метод paint отображает в окне аплета сообщение о том, что идет процесс загрузки.
Когда изображение будет полностью загружено, метод imageUpdate устанавливает значение флага fAllLoaded, равное true, а затем принудительно перерисовывает окно аплета, вызывая метод repaint. При этом метод paint рисует в окне аплета полностью загруженное изображение фона:
g.drawImage(BkgImg, 1, 1,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 2, this);
Метод paint
Сразу после получения управления, метод paint закрашивает окно аплета белым цветом и рисует вокруг него черную рамку.
Затем метод проверяет содержимое флага m_fAllLoaded. Этот флаг установлен в значение true, когда все кадры видеофильма загружены и сброшен в значение false, когда загрузка кадров еще не завершена. Последняя ситуация возникает всегда при первом вызове метода paint.
Если все изображения загружены, метод paint вызывает метод displayImage, определенный в нашем приложении:
if(m_fAllLoaded)
{
displayImage(g);
}
Этот метод, о котором мы еще расскажем подробнее, отображает в окне аплета текущий кадр видеофильма.
Если же кадры видеофильма еще не загружены, в окне аплета отображается соответствующее сообщение:
else
g.drawString("Подождите, идет загрузка...",
10, dimAppWndDimension.height / 2);
Метод paint
После раскрашивания фона окна и рисования рамки метод paint получает с помощью метода getAppletContext ссылку на интерфейс AppletContext:
appContext = getAppletContext();
Далее с помощью этой ссылки и метода getApplets приложение получает список всех аплетов, расположенных в текущем документе HTML;
eApplets = appContext.getApplets();
Вслед за этим метод paint запускает цикл, в котором он получает ссылки на все найденные аплеты:
while(eApplets.hasMoreElements())
{
. . .
}
В этом цикле с помощью метода nextElement приложение получает ссылку на очередной аплет и, после преобразования ее к типу Applet, сохраняет в переменной currentApplet:
Applet currentApplet = (Applet)(eApplets.nextElement());
Для каждого найденного аплета вызывается метод getAppletInfo:
appName = currentApplet.getAppletInfo();
Полученная строка обрезается до первого символа возврата каретки или перевода на новую строку и записывается в переменную appName:
StringTokenizer st;
st = new StringTokenizer(appName, "\r\n");
appName = new String((String)st.nextElement());
Содержимое этой переменной (имя аплета) отображается в окне аплета Inspector со сдвигом по вертикали, который завивит от номера найденного аплета:
g.drawString(appName , 10, 15 * i + 50);
В том случае, если в процессе получения строк информации об аплете был найден аплет Audio, выполняется преобразование типа ссылки на этот аплет и сохранение этой ссылки в поле appAudio:
if(appName.equals("Name: Audio"))
{
appAudio = (Audio)currentApplet;
}
Метод paint
После раскрашивания окна аплета метод paint анализирует содержимое поля m_fStandAlone, определяя режим работы приложения.
Если приложение работает автономно, метод отображает в окне приложения соответствующее сообщение:
g.drawString("Приложение работает автономно", 10, 20);
Затем метод paint определяет и отображает такие параметры среды выполнения приложения, как название архитектуры компьютера, название и версия операционной системы:
str =
"System: " + System.getProperty("os.arch") +
", OS: " + System.getProperty("os.name") +
", ver. " + System.getProperty("os.version");
g.drawString(str, 10, 50);
Строкой ниже отображается путь к каталогу, в котором находится двоичный модуль приложения и путь к каталогу, где находится библиотка классов Java:
str =
"User dir: " + System.getProperty("user.dir") +
", User home: " + System.getProperty("user.home");
g.drawString(str, 10, 65);
В том случае, когда приложение запущено как аплет, последние два параметра не определяются, так как они недоступны для аплетов из соображений безопасности.
Метод paint класса MultiTask2 не
Метод paint класса MultiTask2 не делает ничего нового по сравнению с аналогичным методом предыдущего аплета. Он просто раскрашивает окно аплета в желтый цвет и рисует вокруг него черную рамку.
Метод run класса DrawRectangles
Код метода run выполняется в рамках отдельной задачи. Так как он аналогичен коду метода run предыдущего приложения, то для экономии места мы не будем его описывать.
Метод run вызывает метод wait для синхронизации с другой задачей, поэтому этот метод определен как синхронизированный с помощью ключевого слова synchronized:
public synchronized void run()
{
. . .
}
Внутри метода run организован цикл рисования, который мы уже описывали. После рисования очередного прямоугольника метод run переходит в состояние ожидания извещения, вызывая метод wait:
try
{
Thread.wait();
}
catch (InterruptedException e)
{
}
Метод run класса NotifyTask
Метод run класса NotifyTask периодически разблокирует задачу рисования прямоугольников, вызывая для этого метод notify в цилке с задержкой 30 миллисекунд. Обращение к объекту STask, который хранит ссылку на задачу рисования прямоугольников, выполняется с использованием синхронизации:
synchronized(STask)
{
STask.notify();
}
Метод run
Метод run получает управление при запуске задачи методом start. Если этот метод возвращает управление, соответствующая задача завершает свою работу.
Наша реализация метода run состоит из бесконечного цикла, в котором периодически с задержкой 50 миллисекунд вызывается метод repaint:
public void run()
{
while(true)
{
try
{
repaint();
Thread.sleep(50);
}
catch(InterruptedException e)
{
stop();
}
}
}
Метод repaint вызывает принудительную перерисовку окна аплета, выполняемую методом paint. В нашем приложении этот метод отображает текстовую строку и случайное число.
Для выполнения задержки метод run вызывает метод sleep из класса Thread. Так как метод sleep может вызывать исключение InterruptedException, мы его обрабатываем с помощью операторов try и catch. Если произошло исключение, мы завершаем задачу, вызывая метод stop.
Заметим, что периодическая перерисовка окна аплета может привести к неприятному миганию, поэтому использованный здесь метод периодического обновления содержимого окна аплета нельзя назвать оптимальным. Позже мы рассмотрим другой метод, при котором такой перерисовки не происходит.
Программный код метода run работает в рамках отдельной задачи. Он рисует в окне аплета закрашенные прямоугольники. Прямоугольники имеют случайные координаты, расположение и цвет.
Для того чтобы рисовать, необходимо получить контекст отображения. Так как наша задача, точнее, метод run определен в классе аплета, то он может получить контекст отображения, вызвав метод getGraphics:
Graphics g = getGraphics();
Для рисования нам также нужно знать размеры окна аплета. Мы получаем эти размеры при помощи метода size:
Dimension dimAppWndDimension = size();
Вооружившись контекстом отображения и размерами окна аплета, задача входит в бесконечный цикл рисования прямоугольников.
В качестве генератора случайных чисел мы используем метод random из класса Math, который при каждом вызове возвращает новое случайное число типа double, лежащее в диапазоне значений от 0.0 до 1.0.
Координаты по осям X и Y рисуемого прямоугольника определяются простым умножением случайного числа, полученного от метода random, соответственно, на ширину и высоту окна аплета:
x = (int)(dimAppWndDimension.width * Math.random());
y = (int)(dimAppWndDimension.height * Math.random());
Аналогично определяются размеры прямоугольника, однако чтобы прямоугольники не были слишком крупными, мы делим полученные значения на 2:
width = (int)(dimAppWndDimension.width * Math.random())/2;
height = (int)(dimAppWndDimension.height * Math.random())/2;
Так как случайное число имеет тип double, в обоих случаях мы выполняем явное преобразование результата вычислений к типу int.
Для случайного выбора цвета прямоугольника мы вычисляем отдельные цветовые компоненты, умножая значение, полученное от метода random, на число 255:
rColor = (int)(255 * Math.random());
gColor = (int)(255 * Math.random());
bColor = (int)(255 * Math.random());
Полученные значения цветовых компонент используются в конструкторе Color для получения цвета. Этот цвет устанавливается в контексте отображения методом setColor:
g.setColor(new Color(rColor, gColor, bColor));
Теперь все готово для рисования прямоугольника, которое мы выполняем при помощи метода fillRect:
g.fillRect(x, y, width, height);
После рисования прямоугольника метод run задерживает свою работу на 50 миллисекунд, вызывая метод sleep:
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
stop();
}
Для обработки исключения InterruptedException, которое может возникнуть во время работы этого метода, мы предусмотрели блок try - catch. При возникновении указанного исключения работа задачи останавливается вызовом метода stop.
Внутри метода run мы определили массив строк, проинициализировав его значениями, полученными из параметров аплета:
String s[] = new String[6];
s[0] = m_String1;
s[1] = m_String2;
s[2] = m_String3;
s[3] = m_String4;
s[4] = m_String5;
s[5] = m_String6;
Задача, выполняющаяся в рамках метода run одновременно с кодом аплета, будет по очереди извлекать строки из этого массива и отображать их в нижней части окна аплета.
Так как для рисования строк текста нужно знать контекст отображения, мы получаем его при помощи метода getGraphics:
Graphics g = getGraphics();
Мы также определяем размеры окна аплета, знание которых необходимо для организации сдвига содержимого окна:
Dimension dimAppWndDimension = size();
Перед тем как запустить бесконечный цикл, мы также определяем метрики текущего шрифта и высоту символов шрифта:
FontMetrics fm = g.getFontMetrics();
yChar = fm.getHeight();
В рамках бесконечного цикла мы подсчитываем количество сдвигов (в счетчике ShiftsCounter), а также сдвинутые строки (в счетчике CurrentStr). Заметим, что для обеспечения плавности сдвига мы перемещаем строки по одному пикселу. Когда величина сдвига достигает высоты символов yChar плюс 5, метод run рисует новую строку.
Перед рисованием строки мы выбираем в контекст отображения красный или черный цвет, в зависимости от номера строки:
if(CurrentStr == 0)
g.setColor(Color.red);
else
g.setColor(Color.black);
Вы можете выделять нужные вам строки любым другим способом, например, наклоном или жирным шрифтом.
Для рисования строки мы вызываем метод drawString:
g.drawString(s[CurrentStr],
10, dimAppWndDimension.height - 10);
Строка будет нарисована на десять пикселов выше нижней границы окна аплета.
После рисования строки мы проверяем, последняя она, или нет:
CurrentStr++;
if(CurrentStr > 5)
CurrentStr = 0;
Если строка последняя, мы сбрасываем счетчик текущей строки, после чего перебор строк начнется с самого начала.
Для выполнения свертки мы вызываем метод copyArea, знакомый вам по 30 тому “Библиотеки системного программиста”:
yShift = 1;
g.copyArea(0, yShift + 1,
dimAppWndDimension.width - 1,
dimAppWndDimension.height - 1,
0, -yShift);
Этот метод сдвигает содержимое прямоугольной области экрана, заданной первыми четырьмя параметрами. Величина сдвига определяются двумя последними параметрами метода. В нашем случае сдвиг выполняется по вертикальной оси на значение -1, то есть на один пиксел вверх.
После сдвига освободившаяся область закрашивается желтым цветом:
g.setColor(Color.yellow);
g.fillRect(1, dimAppWndDimension.height - yShift - 1,
dimAppWndDimension.width - 2,
dimAppWndDimension.height - 1);
Далее выполняется задержка на 50 миллисекунд, после чего работа бесконечного цикла возобновляется с самого начала:
Thread.sleep(50);
Перед запуском бесконечного цикла отображения символов строки метод run получает контекст отображения и устанавливает в нем параметры шрифта в соответствии со значениями, переданными аплету через документ HTML.
Прежде всего, метод run получает контекст отображения:
Graphics g = getGraphics();
Затем в этом контексте отображения устанавливается шрифт с жирным, наклонным или обычным начертанием:
if(m_style.equals("BOLD"))
g.setFont(new Font(m_Fnt, Font.BOLD, m_size));
else if(m_style.equals("ITALIC"))
g.setFont(new Font(m_Fnt, Font.ITALIC, m_size));
else
g.setFont(new Font(m_Fnt, Font.PLAIN, m_size));
Обратите внимание, что название шрифта передается конструктору класса Font через первый параметр, а размер символов - через последний.
В зависимости от содержимого поля m_color метод run устанавливает один из трех цветов для отображения символов текстовой строки:
if(m_color.equals("red"))
g.setColor(Color.red);
else if(m_color.equals("green"))
g.setColor(Color.green);
else
g.setColor(Color.black);
Помимо этого, до запуска цикла метод run получает размеры окна аплета и метрики шрифта, только что установленного в контексте отображения:
Dimension dimAppWndDimension = size();
FontMetrics fm = g.getFontMetrics();
В переменную nCurrentChar, хранящую номер текущего отображаемого символа, записывается нулевое значение.
Кроме того, вычисляется позиция для рисования строки по вертикальной оси yPos и устанавливается начальная позиция первого символа строки по горизонтальной оси nCurrentXPos:
int yPos = fm.getHeight() + 5;
int nCurrentXPos = 10;
Далее метод run запускает бесконечный цикл рисования символов.
Первое, что метод run делает в этом цикле, это вычисление ширины текущего символа, сохраняя ее в переменной nCurrentCharWidth:
nCurrentCharWidth =
fm.charWidth(m_Str.charAt(nCurrentChar));
Текущий символ извлекается из строки при помощи метода charAt, определенном в классе String. Ширина извлеченного таким образом символа символа определяется методом charWidth из класса метрик шрифта FontMetrics.
Метод run работает в рамках отдельной задачи. Он занимается последовательным рисованием кадров нашего видеофильма.
Прежде всего метод run записывает нулевое значение в поле m_nCurrImage, хранящее номер текущего отображаемого кадра:
m_nCurrImage = 0;
Далее выполняется проверка, загружены ли все кадры видеофильма, для чего анализируется содержимое флага m_fAllLoaded.
Если изображения не загружены (а в самом начале так оно и есть) метод run перерисовывает окно аплета и получает контекст отображения для этого окна. Затем создается массив объектов Image для хранения кадров видеофильма:
m_Images = new Image[NUM_IMAGES];
Метод run создает также объект класса MediaTracker для ожидания загрузки всех кадров видеофильма:
MediaTracker tracker = new MediaTracker(this);
Далее метод run в цикле загружает изображения и добавляет их в объект класса MediaTracker для того чтобы можно было дождаться загрузки всех кадров:
for (int i = 0; i < NUM_IMAGES; i++)
{
strImage = "images/cdimg0" + ((i < 10) ? "0" : "") +
i + ".gif";
m_Images[i] = getImage(getDocumentBase(), strImage);
tracker.addImage(m_Images[i], 0);
}
Здесь предполагается, что файлы изображений находятся в каталоге images, который, в свою очередь, размещен там же, где и двоичный файл аплета.
Имена файлов, составляющих отдельные кадры, начинаются с префикса cdimg0, вслед за которым идет номер кадра (00, 01, 02, и так далее), и расширение имени .gif.
Ожидание загрузки кадров выполняется с помощью метода waitForAll, о котором мы вам уже рассказывали:
try
{
tracker.waitForAll();
m_fAllLoaded = !tracker.isErrorAny();
}
catch (InterruptedException e)
{
}
После окончания ожидания флаг завершения загрузки устанавливается только в том случае, если метод isErrorAny вернул значение false, то есть если не было никаких ошибок.
Если же произошла ошибка, в окне аплета отображается соответствующее сообщение, после чего работа метода run (и, следовательно, работа созданной для него задачи) заканчивается:
Метод sameFile
С помощью метода sameFile вы можете определить, ссылаются ли два объекта класса URL на один и тот же ресурс, или нет:
public boolean sameFile(URL other);
Если объекты ссылаются на один и тот же ресурс, метод sameFile возвращает значение true, если нет - false.
Метод start
Метод start вызывается, когда пользователь отображает документ HTML, содержащий аплет. Наша реализация этого метода проверяет, создана ли задача перерисовки окна, и, если эта задача не запущена, создает и запускает ее:
public void start()
{
if(m_MultiTask == null)
{
m_MultiTask = new Thread(this);
m_MultiTask.start();
}
}
Первоначально в поле m_MultiTask находится значение null, поэтому при первом вызове метода start всегда создается задача как объекта класса Thread. При этом конструктору с помощью ключевого слова this передается ссылка на наш аплет, поэтому при запуске задачи управление будет передано методу run, определенному в аплете.
Созданная задача не запускается автоматически. Для запуска необходимо вызвать метод start.
Когда пользователь начинает просмотр документа HTML, содержащего наш аплет, метод start создает и запускает задачу. Для создания задачи мы используем оператор new, а для старта задачи - метод start класса Thread:
public void start()
{
if (m_Rectangles == null)
{
m_Rectangles = new Thread(this);
m_Rectangles.start();
}
}
Обратите внимание, что мы передаем конструктору класса Thread параметр this - ссылку на аплет. В результате роль задачи, работающей параллельно с кодом аплета, будет выполнять метод run, определенный в классе аплета.
Ссылка на созданную задачу записывается в поле m_Rectangles.
Метод start основного класса аплета вызывается, когда пользователь отображает страницу сервера Web с аплетом. Наша реализация этого метода создает новую задачу и сохраняет ссылку на нее в поле m_Scroller.
В задачу метода start, который получает управление при отображении окна аплета, входит создание и запуск задачи, отображающий кадры видеофильма с изображением вращающегося компакт-диска:
if (m_CDRotation == null)
{
m_CDRotation = new Thread(this);
m_CDRotation.start();
}
Задача создается как объект класса Thread, причем конструктору передается ссылка на главный класс аплета. Поэтому при запуске задачи управление получит метод run, определенный в классе аплета.
Метод start получает управление при первом запуска аплета, а также когда страница документа появляется вновь после того как пользователь временно переходил к просмотру другой страницы.
Наша реализация метода start возобновляет циклическое проигрывание, если оно выполнялось, когда пользователь покинул страницу с аплетом:
if(fLoopPlay)
auClip.loop();
Этот метод последовательно создает две
Этот метод последовательно создает две задачи и запускает их на выполнение:
public void start()
{
if (m_DrawRectThread == null)
{
m_DrawRectThread = new DrawRectangles(this);
m_DrawRectThread.start();
}
if (m_DrawEllipseThread == null)
{
m_DrawEllipseThread = new DrawEllipse(this);
m_DrawEllipseThread.start();
}
}
Метод start основного класса
Метод start создает и запускает на выполнение две задачи. Первая задача создается как объект класса DrawRectangles, вторая - как объект класса NotifyTask:
if (m_DrawRectThread == null)
{
m_DrawRectThread = new DrawRectangles(this);
m_DrawRectThread.start();
}
if (m_NotifyTaskThread == null)
{
m_NotifyTaskThread = new NotifyTask(m_DrawRectThread);
m_NotifyTaskThread.start();
}
При создании задачи рисования прямоугольников конструктору передается ссылка на аплет. Эта ссылка нужна задаче для определения размеров окна аплета и получения контекста отображения.
Конструктору класса NotifyTask передается ссылка на задачу, работой которой она будет управлять с помощью механизма ожидания извещений.
Метод stop
Когда пользователь покидает страницу с аплетом, имеет смысл остановить нашу задачу, чтобы она не отнимала ресурсов процессора. Остановка выполняется с помощью метода stop:
public void stop()
{
if(m_MultiTask != null)
{
m_MultiTask.stop();
m_MultiTask = null;
}
}
После остановки мы записываем в поле m_MultiTask значение null.
Метод stop нашего аплета не имеет никаких особенностей. Он вызывается, когда пользователь покидает страницу сервера Web с аплетом. В этом случае метод останавливает задачу, вызывая для этого метод stop класса Thread:
public void stop()
{
if (m_Rectangles != null)
{
m_Rectangles.stop();
m_Rectangles = null;
}
}
После остановки в поле m_Rectangles записывается значение null. Это является признаком того, что задача остановлена.
Метод stop основного класса останавливает работу задачи, когда пользователь покидает страницу сервера Web с аплетом, вызывая для этого метод stop.
Метод stop останавливает работу задачи, когда окно аплета исчезает с экрана:
if(m_CDRotation != null)
{
m_CDRotation.stop();
m_CDRotation = null;
}
Для остановки вызывается метод stop.
Если пользователь запустил проигрывание звукового файла в цикле, а затем перешел к просмотру другой страницы, метод stop останавливает циклическое проигрывание:
if(fLoopPlay)
auClip.stop();
Когда пользователь вернется к просмотру нашей страницы, метод start, описанный выше, возобновит проигрывание звукового файла.
Когда пользователь покидает страницу сервера
Когда пользователь покидает страницу сервера Web с аплетом, метод stop класса MultiTask2 последовательно останавливает задачи рисования прямоугольников и эллипсов:
public void stop()
{
if (m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
if (m_DrawEllipseThread == null)
{
m_DrawEllipseThread.stop();
m_DrawEllipseThread = null;
}
}
Метод stop основного класса
Когда пользователь покидает страницу с аплетом, метод stop останавливает работу обеих задач, вызывая для них метод stop из класса Thread:
if (m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
if (m_NotifyTaskThread != null)
{
m_NotifyTaskThread.stop();
m_NotifyTaskThread = null;
}
Метод toExternalForm
Метод toExternalForm возвращает текстовую строку внешнего представления адреса URL, определенного данным объектом класса URL:
public String toExternalForm();
Метод toString
Метод toString возвращает текстовую строку, представляющую данный объект класса URL:
public String toString();
Методы для чтения и записи форматированных данных
Вместо того чтобы записывать в потоки и читать оттуда отдельные байты или массивы байт, программисты обычно предпочитают пользоваться намного более удобными методами классов DataOutputStream и DataInputStream, допускающими форматированный ввод и вывод данных.
Вот, например, какой набор методов можно использовать для записи форматированных данных в поток класса DataOutputStream:
public final void writeBoolean(boolean v);
public final void writeByte(int v);
public final void writeBytes(String s);
public final void writeChar(int v);
public final void writeChars(String s);
public final void writeDouble(double v);
public final void writeFloat(float v);
public final void writeInt(int v);
public final void writeLong(long v);
public final void writeShort(int v);
public final void writeUTF(String s);
Хотя имена методов говорят сами за себя, сделаем замечания относительно применения некоторых из них.
Метод writeByte записывает в поток один байт. Это младший байт слова, которое передается методу через параметр v. В отличие от метода writeByte, метод writeChar записывает в поток двухбайтовое символьное значение (напомним, что в Java символы хранятся с использованием кодировки Unicode и занимают два байта).
Если вам нужно записать в выходной поток текстовую строку, то это можно сделать с помощью методов writeBytes, writeChars или writeUTF. Первый из этих методов записывает в выходной поток только младшие байты символов, а второй - двухбайтовые символы в кодировке Unicode. Метод writeUTF предназначен для записи строки в машинно-независимой кодировке UTF-8.
Все перечисленные выше методы в случае возникновения ошибки создают исключение IOException, которое вы должны обработать.
В классе DataInputStream определены следующие методы, предназначенные для чтения форматированных данных из входного потока:
public final boolean readBoolean();
public final byte readByte();
public final char readChar();
public final double readDouble();
public final float readFloat();
public final void readFully(byte b[]);
public final void readFully(byte b[], int off, int len);
public final int readInt();
public final String readLine();
public final long readLong();
public final short readShort();
public final int readUnsignedByte();
public final int readUnsignedShort();
public final String readUTF();
public final static String readUTF(DataInput in);
public final int skipBytes(int n);
Обратите внимание, что среди этих методов нет тех, что специально предназначены для четния данных, записанных из строк методами writeBytes и writeChars класса DataOutputStream.
Тем не менее, если входной поток состоит из отдельных строк, разделенных символами возврата каретки и перевода строки, то такие строки можно получить методом readLine. Вы также можете воспользоваться методом readFully, который заполняет прочитанными данными массив байт. Этот массив потом будет нетрудно преобразовать в строку типа String, так как в классе String предусмотрен соответствующий конструктор.
Для чтения строк, записанных методом writeUTF вы должны обязательно пользоваться методом readUTF.
Метод skipBytes позволяет пропустить из входного потока заданное количество байт.
Методы класса DataInputStream, предназначенные для чтения данных, могут создавать исключения IOException и EOFException. Первое из них возникает в случае ошибки, а второе - при достижении конца входного потока в процессе чтения.
Методы для настройки параметров разборщика
Ниже мы привели прототипы методов, предназначенных для настройки параметров разборщика:
public void commentChar(int ch);
public void slashSlashComments(boolean flag);
public void slashStarComments(boolean flag);
public void quoteChar(int ch);
public void eolIsSignificant(boolean flag);
public void lowerCaseMode(boolean fl);
public void ordinaryChar(int ch);
public void ordinaryChars(int low, int hi);
public void resetSyntax();
public void parseNumbers();
public void whitespaceChars(int low, int hi);
public void wordChars(int low, int hi);
Несколько методов определяют, будет ли разборщик выделять во входном потоке строки комментария и если будет, то какми образом.
С помощью метода commentChar вы можете указать символ комментария. Если в строке входного потока попадется такой символ, то он и все следующие за ним до конца текущей строки символы будут проигнорированы.
Методы SlashSlashComments и slashStarComments позволяют указать, что для входного текста используются разделители комментариев в виде двойного символа ‘/’ и ‘/* … */’, соответственно. Это соответствует способу указания комментариев в программах, составленных на языках программирования С++ и С. Для включения режима выделения комментариев обоим методам в качетстве параметра необходимо передать значение true, а для отключения - false.
Метод quoteChar позволяет задать символ, который будет использован в качестве кавычек. Когда при разборе потока встречаются слова, взятые в кавычки, они возвращаются программе разбора без кавычек.
Если передать методу eolIsSignificant значение true, разделители строк будут интерпретироваться как отдельные элементы. Если же этому методу передать значение false, разделители строк будут использоваться аналогично пробелам для разделения элементов входного потока.
Метод lowerCaseMode позволяет включить режим, при котором все выделенные элементы будут перекодированы в строчные символы.
Методы ordinaryChar и ordinaryChars позволяют указать символы, которые должны интерпретироваться как обычные, из которых составляются слова или цифры. Например, если передать методу ordinaryChar символ ‘.’, то слово java.io будет восприниматься как один элемент. Если же этого не сделать, то разборщик выделит из него три элемента - слово java, точку ‘.’ и слово io. Метод ordinaryChars позволяет указать диапазон значений символов, которые должны интерпретироваться как обычные.
С помощью метода resetSyntax вы можете указать, что все символы будут рассматриваться, как обычные.
Метод parseNumbers включает режим разбора чисел, при котором распознаются и преобразуются числа в формате с плавающей десятичной точкой.
Метод whitespaceChars задает диапазон значений для символов-разделителей отдельных слов в потоке.
Метод wordChars позволяет указать символы. Которые являются составными частями слов.
Методы для разбора входного потока
После того как вы создали разборщик входного потока на базе класса StreamTokenizer и установили его параметры с помощью описанных выше методов, можно приступать собственно к разборке потока. Обычно для этого организуется цикл, в котором вызывается метод nextToken:
public int nextToken();
Этот метод может вернуть одно из следующих значений:
Значение
Описание
TT_WORD
Из потока было извлечено слово
TT_NUMBER
Из потока было извлечено численное значение
TT_EOL
Обнаружен конец строки. Это значение возвращается только в том случае, если при настройке параметров разборщика был вызван метод eolIsSignficant
TT_EOF
Обнаружен конец файла
Если метод nextToken вернул значение TT_EOF, следует завершить цикл разбора входного потока.
Как извлечь считанные элементы потока?
В классе StreamTokenizer определено три поля:
public String sval;
public double nval;
public int ttype;
Если метод nextToken вернул значение TT_WORD, в поле sval содержится извлеченный элемент в виде текстовой строки. В том случае, когда из входного потока было извлечено числовое значение, оно будет храниться в поле nval типа double. Обычные символы записываются в поле ttype.
Заметим, что если в потоке обнаружены слова, взятые в кавычки, то символ кавычки записывается в поле ttype, а слова - в поле sval. По умолчанию используется символ кавычек ‘”’, однако с помощью метода quoteChar вы можете задать любой другой символ.
При необходимости в процессе разбора вы можете определить номер текущей строки, вызвав для этого метод lineno:
public int lineno();
После вызова метода pushBack следующий вызов метода nextToken приведет к тому, что в поле ttype будет записано текущее значение, а содержимое полей sval и nval не изменится. Прототип метода pushBack приведен ниже:
public void pushBack();
Метод toString возвращает текстовую строку, представляющую текущий элемент, выделенный из потока:
public String toString();
Методы класса Socket
Перечислим наиболее интересные, на наш взгляд, методы класса Socket.
Прежде всего, это методы getInputStream и getOutputStream, предназначенные для создания входного и выходного потока, соответственно:
public InputStream getInputStream();
public OutputStream getOutputStream();
Эти потоки связаны с сокетом и должны быть использованы для передачи данных по каналу связи.
Методы getInetAddress и getPort позволяют определить адрес IP и номер порта, связанные с данным сокетом (для удаленного узла):
public InetAddress getInetAddress();
public int getPort();
Метод getLocalPort возвращает для данного сокета номер локального порта:
public int getLocalPort();
После того как работа с сокетом завершена, его необходимо закрыть методом close:
public void close();
И, наконец, метод toString возвращает текстовую строку, представляющую сокет:
public String toString();
Методы класса StreamTokenizer
Для настройки параметров разборщика StreamTokenizer и получения отдельных элементов входного потока вы должны пользоваться методами, определенными в классе StreamTokenizer. Рассмотрим самые важние из них.
Методы класса Thread
В классе Thread определены три поля, несколько конструкторов и большое количество методов, предназначенных для работы с задачами. Ниже мы привели краткое описание полей, конструкторов и методов.
public class java.lang.Thread
extends java.lang.Object
implements java.lang.Runnable
{
// -----------------------------------------------------
// Поля
// -----------------------------------------------------
// Приоритеты задач
public final static int NORM_PRIORITY; // нормальный
public final static int MAX_PRIORITY; // максимальный
public final static int MIN_PRIORITY; // минимальный
// -----------------------------------------------------
// Конструкторы
// -----------------------------------------------------
// Создание нового объекта Thread
public Thread();
// Создвание нового объекта Thread с указанием объекта,
// для которого будет вызываться метод run
public Thread(Runnable target);
// Аналогично предыдущему, но дополнительно задается
// имя нового объекта Thread
public Thread(Runnable target, String name);
// Создание объекта Thread с указанием его имени
public Thread(String name);
// Создание нового объекта Thread с указанием группы
// задачи и объекта, для которого вызывается метод run
public Thread(ThreadGroup group, Runnable target);
// Аналогично предыдущему, но дополнительно задается
// имя нового объекта Thread
public Thread(ThreadGroup group, Runnable target,
String name);
// Создание нового объекта Thread с указанием группы
// задачи и имени объекта
public Thread(ThreadGroup group, String name);
// -----------------------------------------------------
// Методы
// -----------------------------------------------------
// Текущее количество активных задач в группе, к которой
// принадлежит задача
public static int activeCount();
// Текущей задаче разрешается изменять объект Thread
public void checkAccess();
// Определение количества фреймов в стеке
public int countStackFrames();
// Определение текущей работающей задачи
public static Thread currentThread();
// Принудительное завершение работы задачи
public void destroy();
// Вывод текущего содержимого стека для отладки
public static void dumpStack();
// Получение всех объектов Tread данной группы
public static int enumerate(Thread tarray[]);
// Определение имени задачи
public final String getName();
// Определение текущего приоритета задачи
public final int getPriority();
// Определение группы, к которой принадлежит задача
public final ThreadGroup getThreadGroup();
// Прерывание задачи
public void interrupt();
// Определение, является ли задача прерванной
public static boolean interrupted();
// Определение, выполняется задача или нет
public final boolean isAlive();
// Определение, является ли задача демоном
public final boolean isDaemon();
// Определение, является ли задача прерванной
public boolean isInterrupted();
// Ожидание завершения задачи
public final void join();
// Ожидание завершения задачи в течение заданного времени.
// Время задается в миллисекундах
public final void join(long millis);
// Ожидание завершения задачи в течение заданного времени.
// Время задается в миллисекундах и наносекундах
public final void join(long millis, int nanos);
// Запуск временно приостановленной задачи
public final void resume();
// Метод вызывается в том случае, если задача была
// создана как объект с интерфейсом Runnable
public void run();
// Установка для задачи режима демона
public final void setDaemon(boolean on);
// Устаовка имени задачи
public final void setName(String name);
// Установка приоритета задачи
public final void setPriority(int newPriority);
// Задержка задачи на заднное время.
// Время задается в миллисекундах и наносекундах
public static void sleep(long millis);
// Задержка задачи на заднное время.
// Время задается в миллисекундах и наносекундах
public static void sleep(long millis, int nanos);
// Запуск задачи на выполнение
public void start();
// Остановка выполнения задачи
public final void stop();
// Аварийная остановка выполнения задачи с
// заданным исключением
public final void stop(Throwable obj);
// Приостановка задачи
public final void suspend();
// Строка, представляющая объект-задачу
public String toString();
// Приостановка текущей задачи для того чтобы
// управление было передано другой задаче
public static void yield();
}
С помощью конструкторов вы можете создавать задачи различными способами, указывая при необходимости для них имя и группу. Имя предназначено для идентификации задачи и является необязательным атрибутом. Что же касается групп, то они предназначены для организации защиты задач друг от друга в рамках одного приложения. Подробнее мы расскажем об этом позже.
Методы класса Thread предоставляют все необходимые возможности для управления задачами, в том числе для их синхронизации. Более подробное описание этих методов мы будем приводить по мере изложения материала.
Методы класса URL
Рассмотрим самые интересные методы, определенные в классе URL.
Обращение к полям и методам других аплетов
Теперь вы научились искать аплеты, расположенные в текущем документе HTML, получая список ссылок на соответствующие объекты. Однако для того чтобы получить доступ к полям и методам найденных аплетов, вы должны сделать еще одну вещь. Нужно импортировать в аплет, который занимается поиском, описание класса аплета, к полям и методам которого будет выполняться обращение.
Поясним это.
Все аплеты, как вы знаете, происходят от класса Applet. Они добавляют в этот класс свои поля и методы, а также переопределяют методы из базового класса.
В процессе поиска аплетов метод nextElement возвращает ссылку на объект, принадлежащий к классу Object, который мы можем преобразовать к классу Applet:
Applet currentApplet = (Applet)(eApplets.nextElement());
Однако такое преобразование сможет открыть нам доступ только к тем полям и методам, которые определены в базовом классе. Для того чтобы получить доступ к полям и методам класса найденного аплета, мы должны преобразовать значение, полученное от метода nextElement, к ссылке на класс этого аплета.
Как это можно сделать?
Рассмотрим конкретный пример, использованный нами в приложении Inspector, полные исходные тексты которого вы найдете ниже.
Это приложение управляет работой аплета Audio, описанного в предыдущей главе и предназначенного для проигрывания звукового файла. В классе Audio определено поле auClip, в котором хранится ссылка на интерфейс AudioClip:
public class Audio extends Applet
{
private String m_ClipName = "kaas.au";
private final String PARAM_ClipName = "ClipName";
AudioClip auClip;
. . .
}
Аплет Inspector получает доступ к полю auClip и вызывает методы, предназначенные для управления проигрыванием звукового файла. Таким образом, аплет Inspector пользуется полем auClip, определенным в другом аплете.
Чтобы это стало возможным, в исходном тексте аплета Inspector импортируется класс Audio, как это показано ниже:
import java.applet.*;
import java.awt.*;
import java.util.*;
import Audio;
Когда в процессе поиска аплетов аплет Inspector обнаруживает аплет Audio, он сохраняет ссылку на этот аплет в поле appAudio, выполняя явное преобразование типов:
Audio appAudio = null;
. . .
if(appName.equals("Name: Audio"))
{
appAudio = (Audio)currentApplet;
}
Теперь, пользуясь значением из поля appAudio, можно обращаться к полю auClip, определенному в аплете Audio:
appAudio.auClip.play();
В этом приложении мы создаем
В этом приложении мы создаем на базе класса Thread два класса, один из которых предназначен для создания задачи рисования прямоугольников, а другой - для создания задачи рисования закрашенных эллипсов.
Что же касается основного класса аплета, то он унаследован, как обычно, от класса Applet и не реализует интерфейс Runnable.
Структура приложения Standard очень проста. В нем определен один класс с именем Standard типа public, и один метод с имененм main:
public class Standard
{
public static void main(String args[])
{
. . .
}
}
Напомним, что имена класса и файла .class должны совпадать.
Сразу после запуска автономного приложения Java управление передается функции main.
Внутри этой функции мы определили массив bKbdInput типа byte и строку sOut:
byte bKbdInput[] = new byte[256];
String sOut;
Созданный оператором new массив имеет размер 256 байт и предназначен для хранения строки, введенной пользователем при помощи клавиатуры. В дальнейшем содержимое этого массива преобразуется в строку sOut.
Первое, что делает наше приложение после создания массива, это вывод на консоль текстовой строки приглашения:
System.out.println("Hello, Java!\n" +
"Enter string and press ...");
Здесь вызывается метод println для статического объекта out класса PrintStream, который, как вы знаете, определен в классе System.
На следующем этапе приложение читает из стандартного потока ввода in, вызывая для этого метод read:
System.in.read(bKbdInput);
Стандартный поток ввода связан с клавиатурой, поэтому приложение перейдет в состояние ожидания до тех пор, пока пользователь не введет текстовую строку, нажав после чего клавишу .
Введенная строка отображается на консоли, для чего она записывается в стандартный поток вывода методом println:
System.out.println(sOut);
При выполнении операций с потоками ввода или вывода могут возникать исключения, поэтому в нашем приложении предусмотрены обработчики исключений:
catch(Exception ioe)
{
System.err.println(ioe.toString());
}
При возникновении любого исключения в стандартный поток вывода сообщений об ошибках записывается текстовая строка названия класса возникнувшего исключения.
Для того чтобы вы смогли посмотреть на результаты работы приложения, после отображения на консоли введенной строки приложение вновь вызывается метод read для стандартного потока ввода. Для завершения работы приложения пользователь должен нажать клавишу .
После ввода текстовой строки и ее записи в поле sOut наше приложение создает на базе этой строки объект st класса StringTokenizer:
StringTokenizer st;
st = new StringTokenizer(sOut, ",.; ");
Далее он в цикле получает все элементы строки, вызывая для этого метод nextElement:
while(st.hasMoreElements())
{
str = new String((String)st.nextElement());
System.out.println(str);
}
Для проверки условия завершения цикла вызывается метод hasMoreElements. Когда он возвращает значение false, цикл завершается.
Выделенные в цикле элементы строки записываются в переменную str и отображаются на консоли.
После ввода с клавиатуры пути к файлу или каталогу приложение записывает введенный путь в строку sFilePath класса String.
Так как в этой строке имеется символ конца строки, нам нужно его отрезать. Для этого мы воспользуемся классом StringTokenizer, задав для него в качестве разделителя символ конца строки:
StringTokenizer st;
st = new StringTokenizer(sFilePath, "\r\n");
sFilePath = new String((String)st.nextElement());
Первый же вызов метода nextElement возвращает нам строку пути, которую мы и сохраняем в поле sFilePath.
Далее мы создаем объект класса File, передавая конструктору этого класса строку sFilePath:
File fl = new File(sFilePath);
Так как при вводе пути файла или каталога вы можете допустить ошибку, приложение, прежде чем продолжать свою работу, проверяет существование указанного файла или каталога. Для проверки вызывается метод exists, определенный в классе File:
if(!fl.exists())
{
System.out.println("File not found: " + sFilePath);
}
На следующем этапе приложение проверяет, является ли объект класса File каталогом, вызвая метод isDirectory:
if(fl.isDirectory())
System.out.println("File " + sFilePath + " is directory");
Аналогичная проверка выполняется методом isFile на принадлежность объекта к файлам:
else if (fl.isFile())
System.out.println("File " + sFilePath + " is file");
На последнем этапе приложение определяет различные атрибуты файла или каталога, вызывая соответствующие методы класса File:
System.out.println(
"Parent: " + fl.getParent() +
"\nLength: " + fl.length() +
"\nRead op. available: " + fl.canRead() +
"\nWrite op. available: " + fl.canWrite());
Параметры отображаются на консоли методом println.
В начале своей работы приложение вводит с клавиатуры путь к каталогу и отрезает из полученной строки символ новой строки, пользуясь для этого классом StringTokenizer:
System.out.println("Enter directory path...");
System.in.read(bKbdInput);
sDirPath = new String(bKbdInput, 0);
StringTokenizer st;
st = new StringTokenizer(sDirPath, "\r\n");
sDirPath = new String((String)st.nextElement());
Строка пути записывается в поле sDirPath.
Аналогичным образом вводится и обрабатывается маска, которая записывается в поле sMask.
Далее создается объект класса File, соответствующий каталогу sDirPath, содержимое которого нужно просмотреть:
File fdir = new File(sDirPath);
После этого выполняется проверка существования пути, а также проверка, указывает ли этот путь на каталог. Для проверки мы применяем методы exists и isDirectory, рассмотренные ранее.
Если все нормально, и был указан существующий каталог, приложение анализирует поле маски sMask. В случае пустой маски для получения содержимого каталога мы вызваем метод list без параметров:
if(sMask == null)
dirlist = fdir.list();
Если же маска определена, вызывается второй вариант этого же метода:
else
dirlist = fdir.list(new MaskFilter(sMask));
Здесь в качестве параметра методу list мы передаем вновь созданный объект класса MaskFilter (фильтр), передав соответствующему конструктору строку маски.
В любом случае метод list заполняет полученным списком массив строк dirlist. Содержимое этого массива перебирается в цикле:
for (int i = 0; i < dirlist.length; i++)
{
File f = new File(sDirPath + "\\" + dirlist[i]);
if(f.isFile())
System.out.println(dirlist[i].toLowerCase());
else
System.out.println(dirlist[i]);
}
Для каждого элемента массива мы создаем объект класса File, передавая конструктору путь каталога, добавив к нему разделитель и строку элемента массива. Затем если данная строка соответсвует файлу, а не каталогу, имя выводится строчными буквами. Для преобразования мы вызываем метод toLowerCase, определенный в классе String.
Сразу после запуска приложение запрашивает с консоли текстовую строку адреса URL файла, который необходимо переписать через сеть на локальный диск. После удаления символа перевода строки адрес записывается в поле sURL.
Далее приложение создает объект класса URL, соответствующий введенному адресу:
u = new URL(sURL);
На следующем этапе для объекта URL создается входной поток, для чего вызывается метод openStream:
InputStream is = u.openStream();
Идентификатор этого потока сохраняется в поле is.
Принятый файл будет записан в текущий каталог под именем output.dat. Для этого мы создаем входной буферизованный форматированный поток os, как это показано ниже:
DataOutputStream os = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.dat")));
После знакомства с главой нашей книги, посвященной работе с потоками, эти строки не должны вызывать у вас никаких вопросов.
Операция чтения данных из входного потока и записи в выходной поток выполняется в цикле:
while(true)
{
int nReaded = is.read(buf);
if(nReaded == -1)
break;
os.write(buf, 0, nReaded);
}
Вначале для входного потока вызывается метод read. Он возвращает количество прочитанных байт данных или значение -1, если был достигнут конец потока. В последнем случае цикл прерывается.
Принятые данные размещаются в массиве buf, откуда затем они записываются в выходной поток методом write. Мы записываем в выходной поток столько байт данных, сколько было считано.
После того как файл будет принят и записан в выходной поток, мы закрываем оба потока:
is.close();
os.close();
Приложение ShowChart получает содержимое файла исходных данных для построения круговой диаграммы с помощью класса URL. Как вы увидите, для получения содержимого этого файла оно не создает поток ввода явным образом, как это делало предыдущее приложение (хотя могло бы). Вместо этого оно пользуется методом getContent, определенным в классе URL.
В главном классе аплета определено несколько полей и методов. Рассмотрим эти поля и наиболее важные методы.
Описание исходного текста клиентского приложения SocketClient
Внутри метода main клиентского приложения SocketClient определены переменные для ввода строки с клавиатуры (массив bKbdInput), сокет s класса Socket для работы с сервером SocketServ, входной поток is и выходной поток os, которые связаны с сокетом s.
После вывода на консоль приглашающей строки клиентское приложение создает сокет, вызывая конструктор класса Socket:
s = new Socket("localhost",9999);
В процессе отладки мы запускали сервер и клиент на одном и том же узле, поэтому в качестве адреса сервера указана строка “localhost”. Номер порта сервера SocketServ равен 9999, поэтому мы и передали конструктору это значение.
После создания сокета наше клиентское приложение создает входной и выходной потоки, связанные с этим сокетом:
is = s.getInputStream();
os = s.getOutputStream();
Теперь клиентское приложение готово обмениваться данными с сервером.
Этот обмен выполняется в цикле, условием завершения которого является ввод пользователем строки “quit”.
Внутри цикла приложение читает строку с клавиатуры, записывая ее в массив bKbdInput:
length = System.in.read(bKbdInput);
Количество введенных символов сохраняется в переменной length.
Далее если пользователь ввел строку, а не просто нажал на клавишу , эта строка отображается на консоли и передается серверу:
os.write(bKbdInput, 0, length);
os.flush();
Сразу после передачи сбрасывается буфер выходного потока.
Далее приложение читает ответ, посылаемый сервером, в буфер buf:
length = is.read(buf);
Напомним, что наш сервер посылает клиенту принятую строку в неизменном виде.
Если сервер закрыл канал, то метод read возвращает значение -1. В этом случае мы прерываем цикл ввода и передачи строк:
if(length == -1)
break;
Если же ответ сервера принят успешно, принятые данные записываются в строку str, которая отображается на консоли клиента:
System.out.println(">> " + str);
Перед завершением своей работы клиент закрывает входной и выходной потоки, а также сокет, на котором выполнялась передача данных:
is.close();
os.close();
s.close();
Описание исходного текста приложения CallCGI
Внутри метода main мы определили несколько переменных.
Массив bKbdInput предназначен для хранения строки, введенной с помощью клавиатуры. В переменную length записывается длина этой строки.
Строка str класса String используется в качестве рабочей.
Переменная u класса URL предназначена для хранения ссылки на объект URL, созданный для загрузочного файла программы CGI.
Ссылка на канал связи с программой CGI хранится в переменной с именем c класса URLConnection.
Переменные ps класса PrintStream и is класса DataInputStream хранят ссылки, соответственно, на выходной и входной потоки, через которые наше приложение обменивается данными с программой CGI.
После вывода приглашения на консоль наша программа вводит строку, которая будет передана программе CGI:
length = System.in.read(bKbdInput);
Далее массив bKbdInput преобразуется в строку str и перекодируется в кодировку URL. Эта кодировка была описана нами в 29 томе “Библиотеки системного программиста”. Она выполняется с помощью статического метода encode, определенного в классе URLEncoder:
String StrEncoded = URLEncoder.encode(str);
Перекодированная строка отображается на консоли:
System.out.println("Encoded string: >" + StrEncoded + "<");
На следующем этапе наше приложение создает объект класса URL для загрузочного файла программы CGI:
u = new URL("http://frolov/frolov-cgi/controls.exe");
Здесь предполагается, что программа CGI находится в файле controls.exe, который записан в виртуальный каталог frolov-cgi на сервере Web с адресом http://frolov). Про создание и настройку виртуальных каталогов для размещения расширений сервера Web мы рассказали в 29 томе “Библиотеки системного программиста”.
После создания объекта класса URL мы создаем канал с программой CGI как объект класса URLConnection:
c = u.openConnection();
Пользуясь этим каналом, мы вначале получаем выходной поток методом getOutputStream, а затем на его базе создаем форматированный выходной поток класса PrintStream, удобный для записи в него текстовых строк:
ps = new PrintStream(c.getOutputStream());
Через канал ps наше приложение передает программе CGI строку StrEncoded, а затем закрывает выходной поток, как это показано ниже:
ps.println(StrEncoded);
ps.close();
В этот момент на сервере Web уже запущена программа CGI, и она приняла переданные ей данные. Обработав эти данные, программа CGI записала в стандартный выходной поток динамически созданный ей документ HTML.
Для получения документа наше приложение CallCGI создала входной форматированный поток данных:
is = new DataInputStream(c.getInputStream());
Входной поток создан в два приема. Вначале с помощью метода getInputStream приложение создала обычный входной поток, а затем, на его базе, форматированный входной поток класса DataInputStream.
Получение от программы CGI динамически сформированного ей документа HTML наше приложение выполняет в цикле по строкам.
Строка документа HTML читается из входного форматированного потока методом readLine и записывается в переменную str:
str = is.readLine();
Если в процессе чтения был достигнут конец потока, цикл прерывается:
if(str == null)
break;
Строка, полученная методом readLine, отображается на консоли пиложения:
System.out.println(str);
После завершения цикла входной поток закрывается методом close:
is.close();
Описание исходного текста приложения
Внутри метода main мы создали ссылки на выходной поток OutStream и входной поток InStream:
DataOutputStream OutStream;
DataInputStream InStream;
Кроме того, мы предусмотрели массив для ввода информации из стандартного входного потока:
byte bKbdInput[] = new byte[256];
Способ вывода приглашения и получения строки с клавиатуры ничем не отличается от использованного в предыдущем приложении, поэтому для экономии места мы не будем на нем останавливаться. Скажем только, что введенная строка записывается в поле sOut типа String.
Создание выходного потока для записи строки выполняется следующим образом:
OutStream = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("output.txt")));
Вначале с помощью конструктора создается объект класса FileOutputStream - поток, связанный с файлом output.txt. Далее на базе этого потока создается буферизованный поток типа BufferedOutputStream. И, наконец, на базе буферизованного потока создается форматированный поток OutStream класса DataOutputStream.
Заметим, что конструктор класса FileOutputStream создает файл output.txt, если он не существует, и перезаписывает существующий файл. Если вам нужно исключить случайную перезапись существующего файла, необходимо воспользоваться классом File, о котором мы еще будем рассказывать.
Для записи строки sOut в выходной поток мы вызываем метод writeBytes:
OutStream.writeBytes(sOut);
После записи выполняется сброс содержимого буферов и закрытие потока:
OutStream.flush();
OutStream.close();
При закрытии потока содержимое буферов сбрасывается автоматически. Мы вызвали метод flush только для иллюстрации.
При работе с потоками могут возникать различные исключения. Наш обработчик этих исключений просто записывает в стандратный поток вывода название возникшего исключения:
catch(Exception ioe)
{
System.out.println(ioe.toString());
}
На следующем этапе приложение открывает файл output.txt для чтения буферизованным форматированным потоком:
InStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("output.txt")));
В этой последовательности создания трех объектов различных классов для вас нет ничего нового. Вначале мы создаем поток ввода, связанный с файлом, затем на его базе - буферизованный входной поток, и, наконец, на базе буферизованного входного потока - буферизованный форматированный входной поток.
Для чтения строки из входного потока мы применили метод readLine:
System.out.println(InStream.readLine());
Прочитанная строка сразу отображается на консоли, для чего она записывается в стандартный поток вывода.
После завершения работы со входным потоком мы закрываем его методом close:
InStream.close();
На последнем этапе приложение ожидает ввода с клавиатуры и затем завершает свою работу.
После ввода строки с клавиатуры и записи ее в файл через поток наше приложение создает входной буферизованный поток, как это показано ниже:
InStream = new DataInputStream(
new BufferedInputStream(
new FileInputStream("output.txt")));
Далее для этого потока создается разборщик, который оформлен в отдельном классе TokenizerOfStream, определенном в нашем приложении:
TokenizerOfStream tos = new TokenizerOfStream();
Вслед за этим мы вызываем метод TokenizeIt, определенный в классе TokenizerOfStream, передавая ему в качестве параметра ссылку на входной поток:
tos.TokenizeIt(InStream);
Метод TokenizeIt выполняет разбор входного потока, отображая результаты разбора на консоли. После выполнения разбора входной поток закрывается методом close:
InStream.close();
Самое интересное в нашем приложении связано, очевидно, с классом TokenizerOfStream, поэтому перейдем к его описанию.
В этом классе определен только один метод TokenizeIt:
public void TokenizeIt(InputStream is)
{
. . .
}
Получая в качестве параметра ссылку на входной поток, он прежде всего создает для него разборщик класса StreamTokenizer:
StreamTokenizer stok;
stok = new StreamTokenizer(is);
Настройка параметров разборщика очень проста и сводится к вызовам всего двух методов:
stok.slashSlashComments(true);
stok.ordinaryChar('.');
Метод slashSlashComments включает режим распознавания комментариев в стиле языка программирования С++, а метод ordinaryChar объявляет символ ‘.’ обычным символом.
После настройки запускается цикл разбора входного потока, причем условием завершения цикла является достижение конца этого потока:
while(stok.nextToken() != StreamTokenizer.TT_EOF)
{
. . .
}
В цикле анализируется содержимое поля ttype, которое зависит от типа элемента, обнаруженного во входном потоке:
switch(stok.ttype)
{
case StreamTokenizer.TT_WORD:
{
str = new String("\nTT_WORD >" + stok.sval);
break;
}
case StreamTokenizer.TT_NUMBER:
{
str = "\nTT_NUMBER >" + Double.toString(stok.nval);
Внутри функции main мы определили несколько переменных.
Массив bKbdInput хранит строку, введенную с клавиатуры.
Массив buf используется для хранения строк (команд), которые сервер получает от клиентских приложений.
В переменной s класса DatagramSocket хранится ссылка на датаграмный сокет, который будет использован для приема команд от клиентских приложений.
Переменная pinp класса DatagramPacket хранит пакеты, полученные сервером из сети.
Переменные SrcAddress (класса InetAddress) и SrcPort типа int хранят, соответственно, адрес и порт узла, отправившего пакет.
Первое, что делает сервер - это создание датаграммного сокета:
s = new DatagramSocket(9998);
Конструктору передается номер порта 9998, на котором сервер будет принимать пакеты данных от клиентских приложений.
После создания сокета сервер создает объекта класса DatagramPacket, в который будут записываться приходящие от клиентов пакеты:
pinp = new DatagramPacket(buf, 512);
Конструктор пакета получает ссылку на массив buf, в который нужно будет записывать приходящие по сети данные, и размер этого массива, равный 512 байт.
Далее сервер запускает цикл приема пакетов и отображения их содержимого.
Пакет принимается простым вызовом метода receive из класса DatagramSocket:
s.receive(pinp);
Этот метод блокирует работу вызвавшей его задачи до тех пор, пока по данному сокету не будет получен пакет данных.
Когда пакет будет получен, наше приложение определяет адрес и порт отправителя:
SrcAddress = pinp.getAddress();
SrcPort = pinp.getPort();
Эта информация может пригодиться, если вы будете посылать отправителю ответный пакет.
Наше приложение ответные пакеты не посылает. Оно преобразует принятые данные в текстовую староку класса String, добавляет к этой строке номер порта отправителя и отображает эту информацию на консоли:
System.out.println("> " + str + " < " + "port: " +
SrcPort);
Цикл приема команд завершается, если от клиента пришла строка “quit”:
if(str.equals("quit"))
break;
Перед тем как завершить свою работу, наше приложение закрывает датаграммный сокет, вызывая для этого метод close:
s.close();
Внутри метода main определен массив bKbdInput, предназначенный для хранения данных, введенных с клавиатуры, переменная length, в которой хранится размер этих данных, рабочая строка str класса String, датаграммный сокет s и пакет pout класса DatagramPacket.
Прежде всего приложение определяет адрес узла, на котором оно выполняется, вызывая метод getLocalHost:
InetAddress OutAddress = InetAddress.getLocalHost();
Этот адрес будет использован для формирования передаваемого пакета данных.
Затем клиент создает датаграммный сокет, применяя для этого конструктор без параметров:
s = new DatagramSocket();
Напомним, что в этом случае для сокета выделяется любой свободный порт.
На следующем шаге приложение формирует передаваемый пакет, вызывая конструктор класса DatagramPacket:
pout = new DatagramPacket(bKbdInput, bKbdInput.length,
OutAddress, 9998);
Этому конструктору указывается адрес массива, содержащего введенные с клавиатуры данные, размер этого массива, адрес локального узла, на который нужно передать пакет, и номер порта серверного приложения.
Теперь все готово для запуска цикла передачи команд от клиента к серверу.
В этом цикле выполняется чтение строки с клавиатуры, причем размер прочитанной строки сохраняется в переменной length:
length = System.in.read(bKbdInput);
Далее, если строка не состоит из одного лишь символа перехода на новую строку, она отображается на косоли и посылается серверу методом send:
s.send(pout);
После того как пользователь введет строку “quit”, цикл завершается. Вслед за этим приложение закрывает датаграммный сокет:
s.close();
Описание исходного текста серверного приложения SocketServ
В методе main, получающем управление сразу после запуска приложения, мы определили несколько переменных.
Массив bKbdInput размером 256 байт предназначен для хранения строк, введенных при помощи клавиатуры.
В переменную ss класса ServerSocket будет записана ссылка на объект, предназначенный для установления канала связи через потоковый сокет (но не ссылка на сам сокет):
ServerSocket ss;
Ссылка на сокет, с использованием которого будет происходить передача данных, хранится в переменной с именем s класса Socket:
Socket s;
Кроме того, мы определили переменные is и os, соответственно, классов InputStream и OutputStream:
InputStream is;
OutputStream os;
В эти переменные будут записаны ссылки на входной и выходной поток данных, которые связаны с сокетом.
После отображения на консоли строки названия приложения, метод main создает объект класса ServerSocket, указывая конструктору номер порта 9999:
ss = new ServerSocket(9999);
Конструктор возвращает ссылку на объект, с использованием которого можно установить канал передачи данных с клиентом.
Канал устанавливается методом accept:
s = ss.accept();
Этот метод переводит приложение в состояние ожидания до тех пор, пока не будет установлен канал передачи данных.
Метод accept в случае успешного создания канала передачи данных возвращает ссылку на сокет, с применением которого нужно принимать и передавать данные.
На следующем этапе сервер создает входной и выходной потоки, вызывая для этого методы getInputStream и getOutputStream, соответственно:
is = s.getInputStream();
os = s.getOutputStream();
Далее приложение подготавливает буфер buf для приема данных и определяет переменную length, в которую будет записываться размер принятого блока данных:
byte buf[] = new byte[512];
int lenght;
Теперь все готово для запуска цикла приема и обработки строк от клиентского приложения.
Для чтения строки мы вызываем метод read применительно ко входному потоку:
lenght = is.read(buf);
Этот метод возвращает управление только после того, как все данные будут прочитаны, блокируя приложение на время своей работы. Если такая блокировка нежелательна, вам следует выполнять обмен данными через сокет в отдельной задаче.
Метод read возвращает размер принятого блока данных или -1, если поток исчерпан. Мы воспользовались этим обстоятельством для завершения цикла приема данных:
if(lenght == -1)
break;
После завершения приема блока данных мы преобразуем массив в текстовую строку str класса String, удаляя из нее символ перевода строки, и отображаем результат на консоли сервера:
System.out.println("> " + str);
Затем полученная строка отправляется обратно клиентскому приложению, для чего вызывается метод write:
os.write(buf, 0, lenght);
Методу write передается ссылка на массив, смещение начала данных в этом массиве, равное нулю, и размер принятого блока данных.
Для исключения задержек в передаче данных из-за накопления данных в буфере (при использовании буферизованных потоков) необходимо принудительно сбрасывать содержимое буфреа метдом flush:
os.flush();
И хотя в нашем случае мы не пользуемся буферизованными потоками, мы включили вызов этого метода для примера.
Теперь о завершающих действиях после прерывания цикла получения, отображения и передачи строк.
Наше приложение явням образом закрывает входной и выходной потоки данных, сокет, а также объект класса ServerSocket, с использованием которого был создан канал передачи данных:
is.close();
os.close();
s.close();
ss.close();
Для того чтобы аплет стал
Для того чтобы аплет стал мультизадачным, его класс, который наследуется от класса Applet, дополнительно реализует интерфейс Runnable, как это показано ниже:
public class MultiTask extends Applet implements Runnable
{
. . .
}
Внутри класса определяется поле с именем m_MultiTask типа Thread, которое предназначено для хранения ссылки на объект класса Thread, то есть на задачу:
Thread m_MultiTask = null;
Поле инициализируется значением null. Реальная ссылка на задачу будет сюда записана только после создания задачи.
Рассмотрим теперь методы класса.
Для создания задачи аплет Rectangles реализует интерфейс Runnable, то есть использует второй из описанных нами методов, как и предыдущий аплет.
Ниже мы рассмотрим наиболее важные методы аплета Rectangles.
Для выполнения плавного сдвига строк мы в нашем приложении создаем задачу, которая периодически рисует новые строки в нижней части окна, сдвигая перд этим старые строки вверх.
Основной класс аплета реализует интерфейс Runnable, поэтому для задачи рисования и сдвига строк текста мы не создаем свой класс на базе класса Thread.
Аплет создает задачу отображения символов текстовой строки, для чего он реализует интерфейс Runnable (как и предыдущий аплет). Эта задача рисует отдельные символы текстовой строки, каждый раз определяя их ширину.
Рассмотрим поля и самые важные методы, определенные в классах приложения Synchro.
Мы рассмотрим только самые важные методы нашего аплета - init и paint.
Для работы с базой данных мы создали класс SimpleDBMS, определив в нем конструктор, методы для добавления записей, извлечения записей по их порядковому номеру, а также метод для закрытия базы данных.
Сразу после запуска приложение создает кобъект класса InetAddress для локального узла, вызывая для этого статический метод getLocalHost:
iaLocal = InetAddress.getLocalHost();
Далее для созданного объекта вызывается метод getHostName, возвращающий строку имени локального узла:
System.out.println("Local host name: " +
iaLocal.getHostName());
Это имя отображается на консоли приложения.
Затем приложение определяет адрес IP локального узла, вызывая метод getAddress:
iaLocalIP = iaLocal.getAddress();
Напомним, что этот метод возвращает массив четырех байт адреса IP.
Адрес IP мы отображаем на консоли с помощью метода println:
System.out.println("Local host IP address: " +
(0xff & (int)iaLocalIP[0]) + "." +
(0xff & (int)iaLocalIP[1]) + "." +
(0xff & (int)iaLocalIP[2]) + "." +
(0xff & (int)iaLocalIP[3]));
Заметьте, что байты адреса записваются в массив типа byte как знаковые величины. Для того чтобы отображить их на консоли в виде положительных чисел, мы вначале выполняем явное преобразование к типу int, а затем обнуляем старший байт (так как такое преобразование выполняется с сохранением знака).
Наше приложение демонстрирует также другой способ получения адреса IP для объекта класса InetAddress, который заключается в вызове метода toString:
System.out.println("Local host IP address: " +
iaLocal.toString());
На втором этапе приложение InetAddressDemo вводит строку имени удаленного узла и, после удаления символа перехода на новую строку, пытается создать для введенного имени массив объектов класса InetAddress. Для этого приложение вызывает метод getAllByName:
iaRemoteAll = InetAddress.getAllByName(str);
Содержимое созданного массива отображается в цикле, причем адрес IP извлекается из объектов класса InetAddress методом toString:
for(int i = 0; i < iaRemoteAll.length; i++)
{
System.out.println("Remote host IP address: " +
iaRemoteAll[i].toString());
}
В главном классе нашего аплета определено два поля и несколько методов. Рассмотрим эти поля и самые важные методы.
Опишем наиболее важные методы приложения ImageDrawWait.
Наше приложение переопределяет метод imageUpdate, в котором отслеживает процесс загрузки фонового изображения.
Опишем основные методы, определенные в приложении DrawImageObserver.
Рассмотрим наиболее важные методы нашего приложения.
Обращаем еще раз ваше внимание на то, что в исходном тексте аплета Inspector импортируется описание класса Audio:
import Audio;
Это необходимо для того чтобы аплет мог получить доступ к полю auClip, определенному в этом аплете.
Рассмотрим поля и самые важные методы класса Inspector.
Так как ранее мы уже рассказывали довально подробно о классах приложения Combi, остановимся только на основных моментах.
Так как определение класса CombiFrame расположено в отдельном файле, мы должны импортировать описание этого класса в исходных текстах класса Combi:
import CombiFrame;
Определение адреса IP
Метод getAddress возвращает массив из чеырех байт адреса IP объекта. Байт с нулевым индексом этого массива содержит старший байт адреса IP.
Метод toString возвращает текстовую строку, которая содержит имя узла, разделитель ‘/’ и адрес IP в виде четырех десятичных чисел, разделенных точками.
Определение атрибутов файлов и каталогов
После того как вы создали объект класса File, нетрудно определить атрибуты этого объекта, воспользовавшись соответствующими методами класса File.
Определение длины файла в байтах
Длину файла в байтах можно определить с помощью метода length:
public long length();
Определение имени узла
С помощью метода getHostName вы можете определить имя узла, для которого был создан объект класса InetAddress.
Определение пути к файлу или каталогу
Метод getPath позволяет определить машинно-независимый путь файла или каталога:
public String getPath();
Определение родительского каталога
Если вам нужно определить родительский каталог для объекта класса File, то это можно сделать методом getParent:
public String getParent();
Определение типа объекта - файл или каталог
С помощью методов isDirectory и isFile вы можете проверить, чему соответствует созданный объект класса File - каталогу или файлу:
public boolean isDirectory();
public boolean isFile();
Определение типа указанного пути - абсолютный или относительный
С помощью метода isAbsolute вы можете определить, соответствует ли данный объект класса File файлу или каталогу, заданному абсолютным (полным) путем, либо относительным путем:
public boolean isAbsolute();
Определение времени последней модификации файла или каталога
Для определения времени последней модификации файла или каталога вы можете вызвать метод lastModified:
public long lastModified();
Заметим, однако, что этот метод возвращает время в относительных единицах с момента запуска системы, поэтому его удобно использовать только для относительных сравнений.
Ожидание извещения
Если вам нужно организовать взаимодействие задач таким образом, чтобы одна задача управляла работой другой или других задач, вы можете воспользоваться методами wait, notify и notifyAll, определенными в классе Object.
Метод wait может использоваться либо с параметром, либо без параметра. Этот метод переводит задачу в состояние ожидания, в котором она будет находиться до тех пор, пока для задачи не будет вызван извещающий метод notify, notifyAll, или пока не истечет период времени, указанный в параметре метода wait.
Как пользоваться методами wait, notify и notifyAll?
Метод, который будет переводиться в состояние ожидания, должен быть синхронизированным, то есть его следует описать как synchronized:
public synchronized void run()
{
while (true)
{
. . .
try
{
Thread.wait();
}
catch (InterruptedException e)
{
}
}
}
В этом примере внутри метода run определен цикл, вызывающий метод wait без параметров. Каждый раз при очередном проходе цикла метод run переводится в состояние ожидания до тех пор, пока другая задача не выполнит извещение с помощью метода notify.
Ниже мы привели пример задачи, вызывающией метод notify:
public void run()
{
while (true)
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
}
synchronized(STask)
{
STask.notify();
}
}
}
Эта задача реализована в рамках отдельного класса, конструктору которого передается ссылка на задачу, вызывающую метод wait. Эта ссылка хранится в поле STask.
Обратите внимание, что хотя сам метод run не синхронизированный, вызов метода notify выполняется в синхронизированном режиме. В качестве объекта синхронизации выступает задача, для которой вызывается метод notify.
Ожидание загрузки добавленных изображений
Для того чтобы убедиться, что все изображения загружены, вы можете воспользоваться методом waitForAll. Этот метод инициирует загрузку изображений, а также задержит выполнение вызвавшей его задачи до момента полной загрузки всех изображений, добавленных в объект класса MediaTracker:
try
{
mt.waitForAll();
}
catch (InterruptedException ex)
{
}
Обратите внимание, что метод waitForAll может создавать исключение InterruptedException. Это исключение возникает, если по какой-либо причине процесс ожидания прерывается.
Чаще всего рисование выполняется в отдельной задаче, поэтому метод waitForAll должен вызываться в начале соответствующего метода run. Ниже мы привели исходные тексты приложения ImageDrawWait, в котором такое ожидание выполняется в методе paint, что приводит, однако, к блокировке работы аплета до момента загрузки всех изображений. В данном случае это не критично, так как кроме рисования изображений наш аплет ничего не делает, однако более предпочтительным является выполнение длительных процессов в отдельной задаче.
Ожидание загрузки изображений
Как мы уже говорили, загрузка изображений из сети Internet - длительный процесс, который в среднем идет со скоростью 1 Кбайт в секунду. Поэтому изображения загружаются навигатором в отдельной задаче. При этом метод getImage только создает объект класса Image, а метод drawImage инициирует загрузку изображения и рисует его. Причем если файл изображения имеет большую длину, он будет появляться в окне аплета постепенно по мере загрузки.
Однако в некоторых случаях было бы удобно вначале загрузить изображение полностью, а лишь затем выполнять рисование, чтобы изображение появилось на экране сразу. Кроме того, аплет может рисовать сразу несколько изображений в разных местах своего окна или показывать их по очереди в одном и том же месте для достижения эффекта анимации.
Есть ли способ определить, когда изображение будет загружено полностью?
Есть, и причем целых два. Один из них связан с использованием класса MediaTracker, специально предназначенного для этой цели и достаточно удобного в использовании, другой основан на переопределении одного из методов интерфейса ImageObserver.
Ожидание завершения задачи
С помощью метода join вы можете выполнять ожидание завершения работы задачи, для которой этот метод вызван.
Существует три определения метода join:
public final void join();
public final void join(long millis);
public final void join(long millis, int nanos);
Первый из них выполняет ожидание без ограничения во времени, для второго ожидание будет прервано принудительно через millis миллисекунд, а для третьего - через millis миллисекунд и nanos наносекунд. Учтите, что реально вы не сможете указывать время с точностью до наносекунд, так как дискретность системного таймера компьютера намного больше.
Передача данных между клиентом и сервером
После того как серверное и клиентское приложения создали потоки для приема и передачи данных, оба этих приложения могут читать и писать в канал данных, вызывая методы read и write, определенные в классах InputStream и OutputStream.
Ниже мы представили фрагмент кода, в котором приложение вначале читает данные из входного потока в буфер buf, а затем записывает прочитанные данные в выходной поток:
byte buf[] = new byte[512];
int lenght;
lenght = is.read(buf);
os.write(buf, 0, lenght);
os.flush();
На базе потоков класса InputStream и OutputStream вы можете создать буферизованные потоки и потоки для передачи форматированных данных, о которых мы рассказывали раньше.
Передача данных с использованием сокетов
В библиотеке классов Java есть очень удобное средство, с помощью которых можно организовать взаимодействие между приложениями Java и аплетами, работающими как на одном и том же, так и на разных узлах сети TCP/IP. Это средство, родившееся в мире операционной системы UNIX, - так называемые сокеты (sockets).
Что такое сокеты?
Вы можете представить себе сокеты в виде двух розеток, в которые включен кабель, предназначенный для передачи данных через сеть. Переходя к компьютерной терминологии, скажем, что сокеты - это программный интерфейс, предназначенный для передачи данных между приложениями.
Прежде чем приложение сможет выполнять передачу аили прием данных, оно должно создать сокет, указав при этом адрес узла IP, номер порта, через который будут передаваться данные, и тип сокета.
С адресом узла IP вы уже сталкивались. Номер порта служит для идентификации приложения. Заметим, что существуют так называемые “хорошо известные” (well known) номера портов, зарезервированные для различных приложений. Например, порт с номером 80 зарезервирован для использования серверами Web при обмене данными через протокол HTTP.
Что же касается типов сокетов, то их два - потоковые и датаграммные.
С помощью потоковых сокетов вы можете создавать каналы передачи данных между двумя приложениями Java в виде потоков, которые мы уже рассматривали во второй главе. Потоки могут быть входными или выходными, обычными или форматированными, с использованием или без использования буферизации. Скоро вы убедитесь, что организовать обмен данными между приложениями Java с использованием потоковых сокетов не труднее, чем работать через потоки с обычными файлами.
Заметим, что потоковые сокеты позволяют передавать данные только между двумя приложениями, так как они предполагают создание канала между этими приложениями. Однако иногда нужно обеспечить взаимодействие нескольких клиентских приложений с одним серверным. В этом случае вы можете либо создавать в серверном приложении отдельные задачи и отдельные каналы для каждого клиентского приложения, либо воспользоваться датаграммными сокетами. Последние позволяют передавать данные сразу всем узлам сети, хотя такая возможность редко используется и часто блокируется администраторами сети.
Для передачи данных через датаграммные сокеты вам не нужно создавать канал - данные посылаются непосредственно тому приложению, для которого они предназначены с использованием адреса этого приложения в виде сокета и номера порта. При этом одно клиентское приложение может обмениваться данными с несколькими серверными приложениями или наоборот, одно серверное приложение - с несколькими клиентскими.
К сожалению, датаграммные сокеты не гарантируют доставку передаваемых пакетов данных. Даже если пакеты данных, передаваемые через такие сокеты, дошли до адресата, не гарантируется, что они будут получены в той же самой последовательности, в которой были переданы. Потоковые сокеты, напротив, гарантируют доставку пакетов данных, причем в правильной последовательности.
Причина отстутствия гарантии доставки данных при использовании датаграммных сокетов заключается в использовании такими сокетами протокола UDP, который, в свою очередь, основан на протоколе с негарантированной доставкой IP. Потоковые сокеты работают через протокол гарантированной доставки TCP.
В 23 томе “Библиотеки системного программиста”, который называется “Глобальные сети компьютеров. Практическое введение в Internet, E-Mail, FTP, WWW и HTML, программирование для Windows Sockets” мы уже рассказывали про сокеты в среде операционной системы Microsoft Windows. В этой книге вы найдете примеры приложений, составленных на языке программирования С и работающих как с потоковыми, так и с датаграммными сокетами.
Переименование файлов и каталогов
Для переименования файла или каталога вы должны создать два объекта класса File, один из которых соответствует старому имени, а второй - новому. Затем для перовго из этих объектов нужно вызвать метод renameTo, указав ему в качестве параметра ссылку на второй объект:
public boolean renameTo(File dest);
В случае успеха метод возвращает значение true, при возникновении ошибки - false. Может также возникать исключение SecurityException.
Поля класса Audio
В поле m_ClipName хранится имя звукового файла, которое передается через параметр ClipName из документа HTML. По умолчанию для этого параметра используется значение kaas.au.
Строка PARAM_ClipName хранит имя указанного выше параметра.
Ссылка на интерфейс AudioClip хранится в поле auClip:
AudioClip auClip;
Следующие три поля хранят ссылки на кнопки, предназначенные для управления проигрыванием звукового файла:
Button btPlay;
Button btLoop;
Button btStop;
Поле fLoopPlay типа boolean используется для флага, которым отмечается режим проигрывания звукового файла в цикле.
Поля класса Combi
В классе Combi определено поле с именем m_fStandAlone:
boolean m_fStandAlone = false;
Если приложение работает автономно, в это поле записывается значение true, если как аплет в составе документа HTML - false. По умолчанию это поле инициализируется значением false, однако если приложение запускается автономно, метод main записывает в него значение true:
applet_Combi.m_fStandAlone = true;
Поля класса DrawRectangles
Класс DrawRectangles определен для задачи рисования прямоугольников. В поле g класа хранится контекст отображения окна аплета, а в поле dimAppWndDimension - размеры этого окна. Значения этих полей определяются конструктором класса по ссылке на главный класс аплета.
В поле g класса Graphics хранится контекст отображения окна аплета, определенный конструктором класса DrawRectangles.
Поле dimAppWndDimension хранит размеры окна аплета.
Поля класса HorzScroll
В поле m_HorzScroll хранится сслыка на задачу рсиования строки.
В документе HTML нашему аплету передается несколько параметров, задающих отображаемую строку и определяющих ее внешний вид.
В поле m_Str хранится отображаемая строка.
Шритф, стилевое оформление и размер смиволов строки хранится, соответственно, в полях m_Fnt, m_style и m_size. Цвет символов строки одинаковый и хранится в поле m_color.
Поле m_delay хранит текущее время задержки между отображением отдельных символов строки в миллисекундах.
Поля класса ImageDraw
В классе ImageDraw определено два поля:
Image FloppyDiskImg;
Image CDDiskImg;
В первом из них хранится ссылка на изображение флоппи-диска, во втором - ссылка на изображение компакт-диска.
Поля класса Inspector
В поле appContext хранится ссылка на интерфейс AppletContext, с помощью которого мы будем получать список аплетов.
Этот список будет записан в поле eApplets класса Enumeration.
Когда в процессе поиска наш аплет найдет аплет Audio, то в поле appAudio будет записана ссылка на него.
Кроме того, в классе Inspector определены поля btPlay, btLoop и btStop для хранения ссылок на кнопки управления аплетом Audio.
В классе MultiTask2 мы определили
В классе MultiTask2 мы определили два поля с именами m_DrawRectThread и m_DrawEllipseThread:
DrawRectangles m_DrawRectThread = null;
DrawEllipse m_DrawEllipseThread = null;
Эти поля являются ссылками на классы, соответственно DrawRectangles и DrawEllipse. Первый из них создан для рисования прямоугольников, а второй - эллипсов.
Указанные поля инициализируются занчением null, что соответствует неработающим или несозданным задачам.
Поля класса NotifyTask
В классе NotifyTask мы определили одно поле STask класса Thread, которое хранит ссылку на задачу, работой которой управляет данный класс. Конструктор класса NotifyTask записывает сюда ссылку на задачу рисования прямоугольников.
Поля класса Scroller
В поле m_Scroller записывается ссылка на задачу рисования и сдвига строк текста.
Шесть полей типа String с именами от m_String1 до m_String6 предназначены для хранения сдвигаемых строк текста.
Поля с именами от PARAM_String1 до PARAM_String6 хранят имена параметров аплета.
Поля класса ShowChart
В классе ShowChart определены три поля.
Поле SrcURL класса URL хранит адрес URL файла исходных данных для круговой диаграммы. В поле URLContent типа Object будет переписано содержимое этого файла. И, наконец, в поле errno хранится текущий код ошибки, если она возникла, или нулевое значение, если все операции были выполнены без ошибок.
Поля класса SimpleDBMS
Поля idx dat являются объектами класса RandomAccessFile и представляют собой, соответственно, ссылки на файл индекса и файл данных. Поле idxFilePointer типа long используется как рабочее и хранит текущее смещение в файле.
Поля основного класса аплета
В основном классе аплета определены две ссылки m_DrawRectThread и m_NotifyTaskThread:
DrawRectangles m_DrawRectThread = null;
NotifyTask m_NotifyTaskThread = null;
Первая из них является ссылкой на объект класса DrawRectangles, определенный нами для задачи рисования прямоугольников. Вторая переменная представляет собой ссылку на объект класса NotifyTask, который создан для задачи, управляющей работой задачи рисования.
Получение абсолютного пути к каталогу
Метод getAbsolutePath возвращает абсолютный путь к файлу или каталогу, который может быть машинно-зависимым:
public String getAbsolutePath();
Получение имени файла или каталога
Метод getName возвращает имя файла или каталога для заданного объекта класса File (имя выделяется из пути):
public String getName();
Получение информации о параметрах аплета
Если аплет получает из документа HTML параметры, желательно (но вовсе не обязательно) определить метод getParameterInfo. Этот метод возвращает массив строк описаний параметров, в котором находятся имена и типы параметров, а также строки описаний параметров.
Для примера мы привели фрагмент исходного текста приложения TEXTOUT, описанного нами в 30 томе, посвященном разработке приложений Java:
public String[][] getParameterInfo()
{
String[][] info =
{
{ PARAM_Str1, "String", "Text string to write" },
{ PARAM_Str2, "String", "Text string to write" },
{ PARAM_Str3, "String", "Text string to write" },
. . .
{ PARAM_Font1, "String", "Text font" },
{ PARAM_Font2, "String", "Text font" },
{ PARAM_Font3, "String", "Text font" },
. . .
{ PARAM_Type1, "String", "Font type" },
{ PARAM_Type2, "String", "Font type" },
{ PARAM_Type3, "String", "Font type" },
};
return info;
}
Вызывая метод getParameterInfo для найденного аплета, аплет-инспектор может многое узнать о его параметрах. Эти знания нужны, в частности, для получения значений параметров.
Получение контекста аплетов
Для получения контекста аплетов, или ссылки на интерфейс AppletContext вы должны воспользоваться методом getAppletContext, определенным в классе Applet:
AppletContext appContext;
appContext = getAppletContext();
Далее, вызывая методы, определенные в интерфейсе AppletContext, вы можете получить ссылку на конкретный аплет или список ссылок на все аплеты.
Получение списка содержимого каталога
С помощью метода list вы можете получить список содержимого каталога, соответствующего данному объекту класса File. В классе File предусмотрено два варианта этого метода - без параметра и с параметром:
public String[] list();
public String[] list(FilenameFilter filter);
Первый из этих методв возвращает массив строк с именами содержимого каталога, не включая текущий каталог и родительский каталог. Второй позволяет получить список не всех объектов, хранящихся в каталоге, а только тех, что удовлетворяют условиям, определенным в фильтре filter класса FilenameFilter.
Пример приложения, которое просматривает содержимое каталога и использует при этом фильтр, вы найдете ниже в разделе “Приложение DirList”.
Получение списка всех аплетов
С помощью метода getApplet вы сможете получить ссылку на один аплет, заданный своим именем, но наша задача - получить ссылки на все аплеты, расположенные в текущем документе HTML. Это можно сделать с помощью метода getApplets, определенного следующим образом:
public abstract Enumeration getApplets();
Перед вызовом этого метода вы должны определить список класса Enumeration, например, следующим образом:
Enumeration eApplets;
eApplets = appContext.getApplets();
Получение ссылки на аплет
Метод getApplet возвращает ссылку на аплет, заданный своим именем:
public abstract Applet getApplet(String name);
Это имя должно быть указано в параметре NAME оператора языка HTML, или оно должно передаваться аплету через параметр с именем NAME.
Первый из этих способов демонстрируется ниже:
code=MyApplet.class
id=MyApplet
width=320
height=100
name="TestApplet">
Здесь параметр NAME задает для аплета MyApplet имя TestApplet.
Второй способ был использован нами в следующем фрагменте документа HTML:
code=MyApplet.class
id=MyApplet
width=320
height=100 >
Если аплет с указанным именем имеется в текущем документе HTML, метод getApplet возвращает ссылку на соответствующий объект. Пользуясь этой ссылкой, можно получить доступ к полям и методам, определенным в аплете как public. Если же аплет не найден, метод getApplet возвращает значение null.
Получение строки информации об аплете
Обратите внимание, что в каждом нашем аплете мы определяли метод getAppletInfo:
public String getAppletInfo()
{
return "Name: Inspector\r\n" +
"Author: Alexandr Frolov\r\n" +
"E-mail: frolov@glas.apc.org" +
"WWW: http://www.glasnet.ru/~frolov" +
"Created with Microsoft Visual J++ Version 1.0";
}
Этот метод возвращает строку информации об аплете и, вообще говоря, необязателен. Однако если ваш аплет будет взаимодействовать с другими аплетами, то он может оказать существенную помощь в распознавании аплета.
В самом деле, просматривая в цикле список аплетов, расположенных в текущем документе HTML, аплет-инспектор может вызывать для каждого найденного аплета метод getAppletInfo с целью получения строки, идентифицирующей данный аплет:
appName = currentApplet.getAppletInfo();
Если же аплет не обеспечил метод getAppletInfo, то будет вызван одноименный метод из базового класса Applet, который просто возвращает значение null. Очевидно, это значение нельзя использовать для идентификации аплета.
Получение текстового представления объекта
Метод toString возвращает текстовую строку, представляющую объект класса File:
public String toString();
Получение значений параметров аплета
Для получения значения заданного параметра найденного аплета вы можете воспользоваться методом getParameter:
String sParameter = currentApplet. GetParameter(“name”);
Здесь мы получаем значение параметра с именем NAME.
Получение значения хэш-кода
Метод hashCode возвращает значение хэш-кода, соответствующего объекту File:
public int hashCode();
Потоки в оперативной памяти
Операционные системы Microsoft Windows 95 и Microsoft Windows NT предоставляют возможность для программиста работать с оперативной памятью как с файлом. Это очень удобно во многих случаях, в частности, файлы, отображаемые на память, можно использовать для передачи данных между одновременно работающими задачами и процессами. Подробнее об этом вы можете прочитать в 27 томе “Библиотеки системного программиста”, который называется “Операционная система Microsoft Windows NT для программиста. Часть 2”.
При создании приложений и аплетов Java вы также можете работать с объектами оперативной памяти, как с файлами, а точнее говоря, как с потоками. Так как аплетам запрещено обращаться к файлам, расположенным на локальном диске компьютера, при небходимости создания временных потоков ввода или вывода последние могут быть размещены в оперативной памяти.
Ранее мы отмечали, что в библиотеке классов Java есть три класса, специально предназначенных для создания потоков в оперативной памяти. Это классы ByteArrayOutputStream, ByteArrayInputStream и StringBufferInputStream.
Приложение Audio
Приложение Audio демонстрирует использование интерфейса AudioClip. В его окне (рис. 5.1) имеются три кнопки с названиями Play, Loop и Stop.
Рис. 5.1. Окно аплета Audio
Сразу после запуска аплета кнопка Stop находится в заблокированном состоянии. Если нажать кнопку Play или Loop, начнется, соответственно, однократное проигрывание или проигрывание в цикле файла с именем kaas.au, распложенного в том же каталоге, что и двоичный файл аплета Audio.
Когда начинается проигрывание звукового файла, кнопка Stop разблокируется, что позволяет остановить проигрывание.
Приложение CallCGI
В 29 томе “Библиотеки системного программиста” мы рассказывали о том, как с помощью расширений сервера Web, выполненных на основе интерфейса CGI и ISAPI можно обрабатывать данные из форм, расположенных в документах HTML. В частности, мы привели там исходные тексты программы controls.exe (составленной на языке программирования С), которая динамически создавала и отображала данные, введенные в форме. Внешний вид этой формы показан на рис. 3.6, воспроизведенном нами из указанного тома.
Рис. 3.6. Форма для ввода данных
Программа CGI controls.exe получала данные, введенные пользователем в этой форме, после чего динамически создавала документ HTML, в котором отображала состояние переменных серды, полученные данные в исходном и раскодированном виде, а также список значений полей (рис. 3.7).
Рис. 3.7. Документ HTML, сформрованный динамически программой CGI control.exe
Создавая приложение CallCGI, мы поставили перед собой задачу заменить форму приложением Java, которое вводит с клавиатуры текстовую строку полей и передает ее программе CGI controls.exe. Содержимое динамически сформированного программой CGI документа HTML приложение CallCGI отображает в своем консольном окне, как это показано на рис. 3.8.
Рис. 3.8. Отображение в окне приложения Java содержимого документа HTML, полученного от программы CGI
Приложение CDRotation
Задача отображения видеофильмов в окне Java настолько важна, что Microsoft включил в Visual J++ специальные средства для создания шаблона исходных текстов аплета с анимацией.
Если на третьем шаге системы автоматизированной создания исходных текстов аплетов Java Applet Wizard включить переключатель Yes в поле Would you like your applet to be multi-threaded, а также переключатель Yes в поле Would you like support for animation (рис. 4.3), для вас будут созданы исходные тексты аплета, в окне которого находится изображение земного шара, вращающегося вдоль вертикальной оси.
Рис. 4.3. Включение исходного текста для работы с анимацией в создаваемый аплет
Все, что вам остается сделать, это изменить созданные для вас исходные тексты таким образом, чтобы они соответствовали вашим потребностям. Именно так мы создали исходные тексты приложения CDRotation, в окне которого изображается вращающийся компакт-диск.
Когда будете запускать приложение CDRotation, обратите внимание, что в левом верхнем углу каждого кадра отображается его порядковый номер. Этот номер не нарисован в файлах кадров, а надписывается приложением после рисования очередного кадра. Такое невозможно, если располагать в документе HTML файл AVI или многосекционный файл GIF.
Приложение Combi
Приложение Combi имеет описанную выше структуру и потому способно работать как автономно, так и под управлением навигатора Internet.
После запуска приложение определяет и отображает в своем окне текущий режим работы, а также некоторую информацию о среде выполнения.
На рис. 7.1 показан внешний вид окна этого приложения, когда оно работает автономно.
Рис. 7.1. Окно автономно работающего приложения Combi
Приложение определяет платформу, на которой оно работает, название и версию операционной системы, каталог, в котором находится двоичный модуль приложения, и каталог, в котором расположены классы Java. Кроме того, сразу после запуска автономное приложение Combi, если оно выполняется в среде операционной системы Windows 95 или Windows NT, запускает программу калькулятора calc.exe, входящую в комплект этой операционной системы.
Когда приложение Combi работает как аплет, встроенный в документ HTML, его окно имеет вид, показанный на рис. 7.2.
Рис. 7.2. Окно приложения Combi, встроенного в документ HTML
Из-за ограничений, которые накладываются на аплеты, в данном случае нашему приложению доступна не вся информация о среде выполнения.
Кроме того, будучи загруженным с сервера Web, приложение не сможет запустить программу калькулятора, так как эта возможность для аплетов заблокирована. Однако при работе аплета в среде Microsoft Visual J++ запуск приложений (а также другие действия) разрешается, поэтому на экране появится окно клаькулятора.
Приложение DirectFileAccess
Для иллюстрации способов работы с классом RandomAccessFile мы подготовили приложение DirectFileAccess, в котором создается небольшая база данных. Эта база данных состоит из двух файлов: файла данных и файла индекса.
В файле данных хранятся записи, сосотящие из двух полей - текстового и числового. Текстовое поле с названием name хранит строки, закрытые смиволами конца строки “\r\n”, а числовое с названием account - значения типа int.
Дамп файла данных, создаваемого при первом запуске приложения DirectFileAccess, приведен на рис. 2.11.
Рис. 2.11. Дамп файла данных
Из этого дампа видно, что после первого запуска приложения в файле данных имеются следующие записи:
Номер записи
Смещение в файле данных
Поле name
Поле account
0
0
Ivanov
1000
1
12
Petrov
2000
2
24
Sidoroff
3000
При последующих запусках каждый раз в файл данных будут добавляться приведенные выше записи.
Так как поле name имеет переменную длину, для обеспечения возможности прямого доступа к записи по ее номеру необходимо где-то хранить смещения всех записей. Мы это делаем в файле индексов, дамп которого на момент после первого запуска приложения представлен на рис. 2.12.
Рис. 2.12. Дамп файла индекса
Файл индексов хранит 8-байтовые смещения записей файла данных в формате long. Зная номер записи, можнор легко вычислить смещение в файле индексов, по которому хранится смещение нужной записи в файле данных. Если извлечь это смещение, то можно выполнить позиционирование в файле данных с целью чтения нужной записи, что и делает наше приложение.
После добавления трех записей в базу данных приложение извлекает три записи в обратном порядке, то есть сначала запись с номером 2, затем с номером 1, и, наконец, с номером 0. Извлеченные записи отображаются в консольном окне приложения (рис. 2.13).
Рис. 2.13. Отображение записей базы данных приложением DirectFileAccess
Приложение DirList
В приложении DirList мы используем класс File для получения списка всех файлов и каталогов, расположенных в заданном каталоге.
После запуска приложение DirList предлагает ввести путь к каталогу и маску для имени файла (рис. 2.9).
Рис. 2.9. Работа приложения DirList
Если вместо маски задать символ ‘*’, как мы сделали это на рис. 2.9, приложение выведет полный список файлов и каталогов, выделив каталоги прописными буквами. В том случае, если будет задна другая маска, в окне появятся только такие файлы, которые содержат эту маску как подстроку (рис. 2.10).
Рис. 2.10. Просмотр содержимого каталога c:\dos с маской com
Приложение DrawImageObserver
Приложение DrawImageObserver рисует в своем окне изображение фона, такое же, как и в предыдущем приложении. При этом для ожидания процесса загрузки изображения перед рисованием фона мы используем интерфейс ImageObserver.
Если бы изображение фона, имеющее значительный размер, рисовалось без ожидания его загрузки, оно появлялось бы в окне аплета по частям. Наше приложение рисует его сразу, так как дожидается полной загрузки.
Приложение FileInfo
В приложении FileInfo мы демонстрируем способы работы с классом File.
После запуска наше приложение предлагает ввести путь к файлу (рис.2.8). Вы также можете ввести путь к каталогу.
Рис. 2.8. Работа приложения FileInfo
Далее приложение создает объект класса File, передавая введенную строку соответствующему конструктору, а затем, если указанный файл или каталог существует, отображает его параметры.
На рис. 2.8 видно, что в ответ на прглашение был введен путь к файлу autoexec.bat. Приложение вывело родительский каталог, в котором находится этот файл (диск c:), длину файла в байтах (235 байт), а также сообщило нам, что для файла разрешены операции чтения и записи.
Приложение HorzScroll
Практически в каждой книге, посвященной программированию на языке Java, вы найдете исходные тексты приложения, выполняющие горизонтальную прокрутку строки текста. Эффект бегущей строки достаточно широко используется для привлечения внимания пользователя.
Реализация эффекта бегущей его достаточно проста. Аплет создает задачу, которая периодически перерисовывает окно, вызывая метод repaint. Метод paint отображает строку в окне, каждый раз изменяя ее начальные координаты для получения эффекта сдвига.
Мы уверены, что вы сами легко справитесь с задачей создания бегущей строки, поэтому предложим вам кое-что иное. А именно, мы подготовили приложение, которое выписывает текстовую строку в своем окне последовательно буква за буквой. Когда вся строка будет нарисована, окно очищается и затем процесс возобновляется вновь (рис. 1.5).
Рис. 1.5. Окно аплета HorzScroll
В чем сложность создания такого аплета?
Главным образом в том, что все символы пропорционального шрифта имеют разную ширину, и вам нужно правильно вычислять координаты каждого нового символа.
Приложение ImageDraw
Теперь мы знаем все необходимое, чтобы приступить к рисованию растровых изображений в окне аплета. Приложение ImageDraw, о котором мы сейчас расскажем, рисует в своем окне четыре изображения: два изображения флоппи-диска и два - компакт-диска (рис. 4.1).
Рис. 4.1. Рисование растровых изображений в окне приложения ImageDraw
В верхнем левом углу окна аплета нарисованы исходные изображения. Справа вверху изображение компакт-диска нарисовано растянутым по горизонтали. Нижнюю часть окна аплета занимает пропорционально увеличенный рисунок флоппи-диска.
Приложение ImageDrawWait
В приложении ImageDrawWait мы демонстрируем использование класса MediaTracker для ожидания процесса завершения загрузки изображений, показанных на рис. 4.1. Эти изображения рисуются приложением ImageDrawWait не на белом фоне, а на фоне другого изображения.
Файл изображение фона имеет значительный размер, поэтому без применения техники ожидания завершения загрузки он будет появляться в окне по частям (именно так работает метод imageUpdate, определенный в классе Applet), а затем будут нарисованы остальные изображения. Наше приложение сначала дожидается завершения загрузки всех изображений, а затем рисует их, поэтому все изображения появятся практически одновременно и целиком.
В процессе загрузки окно приложения ImageDrawWait отображает сообщение о ходе загрузки (рис. 4.2).
Рис. 4.2. Сообщение о ходе процесса загрузки изображений
После загрузки в окне аплета рисуется изображение фона
Приложение InetAddressDemo
Приложение InetAddressDemo отображает имя и адрес IP локального узла, а затем запрашивает имя удаленного узла. Еси такой узел существует, для него определяется и отображается на консоли список адресов IP (рис.3.1).
Рис. 3.1. Работа приложения InetAddressDemo
Если же указано имя несуществующего узла, возникает исключение UnknownHostException, о чем на консоль выводится сообщение.
Приложение Inspector
Аплет Inspector располагается в одном документе HTML с приложениями Audio и Rectangles, которые уже были описаны в нашей книге (рис. 6.1).
Рис. 6.1. Документ HTML, в котором расположены три аплета - Inspector, Audio и Rectangles
В верхней части окна аплета Inspector расположены кнопки, дублирующие одноименные кнопки аплета Audio. С их помощью можно заставить аплет Audio проигрывать файл в однократном режиме или в цикле, а также остановить проигрывание.
В нижней части аплета Inspector отображается список имен аплетов, найденных в текущем документе HTML. Для списка используются первые строки описаний аплетов, полученные методом getAppletInfo.
Приложение MemStream
Аплет MemStream создает два потока в оперативной памяти - выходной и входной. Вначале во время инициализации метод init создает выходной поток и записывает в него текстовую строку “Hello, Java!”. Содержимое этого потока затем копируется в массив, и на базе этого массива создается входной поток.
Во время рисования окна аплета метод paint создает из только что упомянутого массива входной поток, читает из этого потока одну строку и отображает ее в окне аплета.
Аплет не может работать с обычными локальными файлами, поэтому для выполнения описанных выше действий необходимы потоки в оперативной памяти.
Приложение MultiTask
Система автоматизированной разработки аплетов Microsoft Visual J++ позволяет указать, что создаваемый аплет будет мультизадачным. Для этого на третьем шаге в поле Would you like your applet to be multi-threaded следует включить переключатель Yes (рис. 1.1).
Рис. 1.1. Добавление мультизадачности в создаваемый аплет
Включив указанный переключатель, выключите пока переключатель Would you like support for animation - анимацией мы займемся немного позже.
После завершения процедуры создания заготовки мультизадачного аплета выполните трансляцию и запустите аплет на выполнение. На экране появится текстовая строка с числом, значение которого быстро изменяется случайным образом.
Поставим теперь перед собой другую цель - создать аплет, запускающий на выполнение сразу две задачи. Наше следующее приложение MultiTask2 запускает одну задачу для рисования прямоугольников, а другую - для рисования закрашенных эллипсов (рис. 1.3).
Рис. 1.3. Окно аплета MultiTask2
Расположение, размеры и цвет прямоугольников и эллипсов выбирается случайным образом.
Приложение Rectangles
В предыдущем приложении задача выполняла периодическую перерисовку окна аплета, вызывая из метода run метод repaint. Такая методика приводит к сильному мерцанию окна аплета и поэтому во многих случаях нежелательна. Приложение Rectangles, постоянно отображающее в своем окне прямоугольники случайного размера, расположения и цвета (рис.1.2), использует другой способ. Оно запускает задачу, которая рисует в окне аплета непосредственно.
Рис. 1.2. Окно аплета Rectangles
В результате в каждый момент времени перерисовывается только часть окна аплета и мерцание отсутствует.
Приложение Scroller
Приложения, рассмотренные выше, демонстрируют различные методы реализации мультизадачности в Java, но едва ли вы найдете для них применение (разве лишь гипнотизирование пользователей). Ниже мы приведем исходные тексты приложения Scroller, которое имеет некоторую практическую ценность.
В своем окне приложение Scroller показывает строки текста, медленно всплывающие вверх (рис. 1.4). Вы можете использовать этот аплет для размещения рекламной информации на своем сервере. Всплывающий текст (как и всякое движение на сранице сервера Web) будет привлекать внимание пользователя.
Рис. 1.4. Окно аплета Scroller
Строки (в количестве 6 штук) можно задавать в параметрах аплета, редактируя текст документа HTML, содержащего этот аплет. Первая строка выделяется красным цветом.
Приложение ShowChart
Попробуем теперь на практике применить технологию передачи файлов из каталога сервера Web в аплет для локальной обработки. Наше следующее приложение с названием ShowChart получает небольшой текстовый файл с исходными данными для построения круговой диаграммы, содержимое которого представлено ниже:
10,20,5,35,11,10,3,6,80,10,20,5,35,11,10,3,6,80
В этом файле находятся численные значения углов для отдельных секторов диаграммы, причем сумма этих значений равна 360 градусам. Наш аплет принимает этот файл через сеть и рисует круговую диаграмму, показанную на рис. 3.2.
Рис. 3.2. Круговая диаграмма, построенная на базе исходных данных, полученных через сеть
Файл исходных данных занимает всего 49 байт, поэтому он передается по сети очень быстро. Если бы мы передавали графическое изображение этой диаграммы, статическое или динамическое, подготовленное, например, расширением сервера CGI или ISAPI, объем передаваемых по сети данных был бы намного больше.
Приложение Standard
Приложение Standard демонстрирует способы работы со стандартными потоками Java. Это консольное приложение, а не аплет.
При запуске приложение Standard выводит строку Hello, Java! И приглашение для ввода строки (рис. 2.4).
Рис. 2.4. Консольное окно приложения Standard
Если ввести любую текстовую строку и нажать клавишу , введенная строка появится на консоли. Далее появится сообщение о том, что для завершения работы приложения нужно снова нажать клавишу .
Приложение StreamDemo
Приложение StreamDemo было создано нами специально для того чтобы продемонстрировать наиболее распространенный способ работы с файлами через буферизованные форматированные потоки.
Действия, выполняемые этим приложением, достаточно просты. Вначале приложение выводит на консоль приглашение для ввода строки. Затем введенная строка записывается в файл через выходной буферизованный форматированный поток. После этого созданный файл читается через входной буферизованный поток. Его содержимое (введенная ранее строка) отображается на экране.
Приложение StreamToken
В приложении StreamToken мы демонстрируем использование класса StreamTokenizer для разбора входного потока.
Вначале приложение запрашивает у пользователя строку для разбора, записывая ее в файл. Затем этот файл открывается для чтения буферизованным потоком и разбирается на составные элементы. Каждый такой элемент выводится в отдельной строке, как это показано на рис. 2.6.
Рис. 2.6. Разбор входного потока в приложении StreamToken
Обратите внимание, что в процессе разбора значение 3.14 было воспринято как числовое, а 3,14 - нет. Это потому, что при настройке разборщика мы указали, что символ ‘.’ является обычным.
Приложение StringToken
Приложение StringToken получает одну строку из стандартного потока ввода и выполняет ее разбор с помощью класса StringTokenizer. Отдельные элементы строки выводятся на консоль в столбик (рис. 2.7).
Рис. 2.7. Разбор строки в приложении StringToken
В качестве разделителя заданы символы ",.; ", то есть запятая, точка, точка с запятой и пробел.
Приложение Synchro
Для иллюстрации способа синхронизации задач с помощью методов wait и notify мы подготовили приложение Synchro. Внешне окно аплета этого приложения выглядит точно так же, как и окно аплета Rectangles (рис. 1.2).
Напомним, что приложение Rectangles постоянно рисует закрашенные прямоугольники случайного размера, расположения и цвета, для чего в методе run организован бесконечный цикл с задержкой.
Приложение Synchro решает ту же самую задачу немного другим способом. Оно создает две задачи. Первая задача рисует в цикле прямоугольники, однако вместо задержки она ожидает извещение от другой задачи, вызывая для этого функцию wait. Вторая задача, опять же в цикле, создает извещения, вызывая с задержкой метод notify. Таким образом, вторая задача как бы управляет работой первой задачи.
Приложение URLDemo
В качестве практического примера применения класса URL мы создали приложение URLDemo. Это приложение вводит с консоли адрес URL текстового или двоичного файла, расположенного на сервере Web и создает для этого файла входной поток. С использованием данного потока приложение копирует файл на локальный диск компьютера в текущий каталог.
Приложения DatagramServer и DatagramClient
Приложения DatagramServer и DatagramClient иллюстрируют применение датаграммных сокетов для передачи данных от нескольких копий одного и того же клиента одному серверу с известным адресом и номером порта.
Клиентские приложения посылают серверу строки, которые пользователь вводит с клавиатуры. Сервер принимает эти строки, отображая их в своем консольном окне вместе с номером порта клиента (рис. 3.4).
Рис. 3.4. Передача данных между приложениями DatagramClient и DatagramServer через датаграммный сокет
Когда с консоли клиента будет введена строка “quit”, этот клиент и сервер завершает свою работу. Работа остальных клиентов также может быть завершена подобным образом, причем независимо от того, работает сервер, или нет.
Наши клиенты не получают от сервера никакого подтверждения в ответ на переданные ему пакеты. Вы можете изменить программу клиента, добавив такую возможность. Однако учтите, что так как датаграммные сокеты не гарантируют доставки пакетов, ожидание ответа может продлиться бесконечно долго.
Чтобы избежать этого, ваше приложение должно выполнять работу с сервером в отдельной задаче, причем главная задача должна ждать ответ ограниченное время. Все что вам нужно, чтобы реализовать работу подобным образом, вы найдете в первой главе нашей книги, посвященной мультизадачности в приложениях Java.
Приложения SocketServ и SocketClient
В качестве примера мы приведем исходные тексты двух приложений Java, работающих с потоковыми сокетами. Одно из этих приложений называется SocketServ и выполняет роль сервера, второе называется SocketClient и служит клиентом.
Приложение SocketServ выводит на консоль строку “Socket Server Application” и затем переходит в состояние ожидания соединения с клиентским приложением SocketClient.
Приложение SocketClient устанавливает соединение с сервером SocketServ, используя потоковый сокет с номером 9999 (этот номер выбран нами произвольно). Далее клиентское приложение выводит на свою консоль приглашение для ввода строк. Введенные строки отображаются на консоли и передаются серверному приложению. Сервер, получив строку, отображает ее в своем окне и посылает обратно клиенту. Клиент выводит полученную от сервера строку на консоли.
Когда пользователь вводит строку “quit”, цикл ввода и передачи строк завершается.
Весь процесс показан на рис. 3.3.
Рис. 3.3. Передача данных между приложениями SocketClient и SocketServ через потоковый сокет
Здесь в окне клиентского приложения мы ввели несколько строк, причем последняя строка была строкой “quit”, завершившая работу приложений.
Применение интерфейса ImageObserver
Второй способ ожидания завершения процесса загрузки изображений связан с интерфейсом ImageObserver. Определение этого интерфейса не занимает много места, поэтому мы приведем его полностью:
public interface java.awt.image.ImageObserver
{
// -------------------------------------------------------
// Биты флагов для параметра infoflags метода imageUpdate
// -------------------------------------------------------
public final static int ABORT;
public final static int ALLBITS;
public final static int ERROR;
public final static int FRAMEBITS;
public final static int HEIGHT;
public final static int PROPERTIES;
public final static int SOMEBITS;
public final static int WIDTH;
// -------------------------------------------------------
// Метод imageUpdate
// -------------------------------------------------------
public abstract boolean
imageUpdate(Image img, int infoflags, int x, int y,
int width, int height);
}
Как видите, в интерфейсе ImageObserver определен единственный метод imageUpdate и набор битовых флагов для этого метода.
Класс Component, от которого происходит класс Applet, реализует интерфейс ImageObserver:
public abstract class java.awt.Component
extends java.lang.Object
implements java.awt.image.ImageObserver
{
. . .
}
Этот интерфейс используется для отслеживания процесса загрузки и перерисовки изображений и других компонент, расположенных внутри компонента. В частности, он используется для отслеживания загрузки и рисования растровых изображений в окне аплета, чем мы и воспользуемся.
В процессе загрузки вызывается метод imageUpdate, поэтому чтобы отслеживать загрузку изображений, наш аплет должен переопределить этот метод.
Процедура ожидания загрузки изображений достаточно проста.
Прежде всего, аплет должен передать в последнем параметре методу drawImage ссылку на интерфейс ImageObserver, который будет применяться для отслеживания процесса загрузки:
g.drawImage(Img, x, y, width, height, this);
Здесь в качестве ссылки на интерфейс ImageObserver мы передали значение this. При этом будет применен интерфейс нашего аплета. Соответственно, нам нужно определить в классе аплета метод imageUpdate, который будет вызываться в процессе загрузки изображений.
Ниже мы привели возможный вариант реализации этого метода, который позже будет описан в деталях:
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h)
{
// Проверяем, все ли биты изображения загружены
fAllLoaded = ((flags & ALLBITS) != 0);
// Если все, перерисовываем окно
if(fAllLoaded)
repaint();
// Если все биты загружены, дальнейшие вызовы
// метода imageUpdate не нужны
return !fAllLoaded;
}
Через первый параметр img методу imageUpdate передается ссылка на изображение, загрузка которого отслеживается.
Параметр flags отражает состояние процесса загрузки.
Через остальные параметры x, y, w и h передаются, соответственно, координаты и размеры изображения.
Основное, что должен делать метод imageUpdate для отслеживания процесса загрузки - это проверять флаги flags, дожидаясь установки нужных флагов.
Флаги определены следующим образом:
public final static int WIDTH;
public final static int HEIGHT = 2;
public final static int PROPERTIES = 4;
public final static int SOMEBITS = 8;
public final static int FRAMEBITS = 16;
public final static int ALLBITS = 32;
public final static int ERROR = 64;
public final static int ABORT = 128;
Ниже мы привели краткое описание перечисленных выше флагов.
Флаг
Описание
WIDTH
Изображение загружено настолько, что стала доступна его ширина. Значение ширины изображения можно получить из параметра w метода imageUpdate
HEIGHT
Аналогично предыдущему, но для высоты изображения. Высоту изображения можно получить из параметра h метода imageUpdate
PROPERTIES
Стали доступны свойства изображения, которые можно получить методом getProperty класса Image. В нашей книге мы опустили описание этого метода
SOMEBITS
Стали доступны биты изображения для рисования в масштабе. Через параметры x, y, h и w передаются координаты и размеры прямоугольной области, которая ограничивает загруженную часть изображения
FRAMEBITS
Загружен очередной фрейм изображения, состоящего из нескольких фреймов. Параметры x, y, h и w следует игнорировать
ALLBITS
Изображение загружено полностью. Параметры x, y, h и w следует игнорировать
ERROR
При загрузке произошла ошибка
ABORT
Загрузка изображения была прервана или отменена
Анализируя состояние флагов, метод imageUpdate может следить за ходом загрузки изображений, отображая, например, процент завершения процесса загрузки или выполняя какие-либо другие действия.
Если вам нужно только дождаться завершения процесса загрузки, достаточно использовать флаг ALLBITS. Для проверки ошибок воспользуйтесь флагами ERROR и ABORT.
Применение класса MediaTracker
Для того чтобы выполнить ожидание загрузки нескольких изображений, проще воспользоваться классом MediaTracker, а не интерфейсом ImageObserver.
Как это сделать?
Обычно метод init аплета создает объект класса MediaTracker с помощью конструктора и добавляет в него все изображения, загрузки которых необходимо дождаться.
Применение мультизадачности для анимации
Одно из наиболее распространенных применений аплетов - это создание анимационных эффектов типа бегущей строки, мерцающих огней или аналогичных, привлекающих внимание пользователя. Для того чтобы достичь такого эффекта, необходим какой либо механизм, позволяющий выполнять перерисовку всего окна аплета или его части периодически с заданным временным интервалом.
Работа аплетов, так же как и обычных приложений операционной системы Microsoft Windows, основана на обработке событий. Для классического приложения Microsoft Windows событие - это приход сообщения в функцию окна. Основной класс аплета обрабатывает события, переопределяя те или иные методы базового класса Applet.
Проблема с периодическим обновлением окна аплета возникает из-за того, что в языке Java не предусмотрено никакого механизма для создания генератора событий, способного вызывать какой-либо метод класса аплета с заданным интервалом времени. Вы не можете поступить так, как поступали в этой ситуации, разрабатывая обычные приложения Microsoft Windows - создать таймер и организовать обработку периодически поступающих от него сообщений WM_TIMER.
Напомним, что перерисовка окна аплета выполняется методом paint, который вызывается виртуальной машиной Java асинхронно по отношению к выполнению другого кода аплета.
Можно ли воспользоваться методом paint для периодической перерисовки окна аплета, организовав в нем, например, бесконечный цикл с задержкой?
К сожалению, так поступать ни в коем случае нельзя. Метод paint после перерисовки окна аплета должен сразу возвратить управление, иначе работа аплета будет заблокирована.
Единственный выход из создавшейся ситуации - создание задачи (или нескльких задач), которые будут выполнять рисование в окне аплета асинхронно по отношению к работе кода аплета. Например, вы можете создать задачу, которая периодически обновляет окно аплета, вызывая для этого метод repaint, или рисовать из задачи непосредственно в окне аплета, получив предварительно для этого окна контекст отображения. В примерах аплетов, приведенных в нашей книге, мы будем использовать оба способа.
Принудительный сброс буферов
Еще один важный момент связан с буферизованными потоками. Как мы уже говорили, буферизация ускоряет работу приложений с потоками, так как при ее использовании сокращается количество обращений к системе ввода/вывода. Вы можете постепенно в течении дня добавлять в поток данные по одному байту, и только к вечеру эти данные будут физически записаны в файл на диске.
Во многих случаях, однако, приложение должно, не отказываясь совсем от буферизации, выполнять принудительную запись буферов в файл. Это можно сделать с помощью метода flush.
Приоритеты задач в приложениях Java
Если процесс создал несколько задач, то все они выполняются параллельно, причем время центрального процессора (или нескольких центральных процессоров в мультипроцессорных системах) распределяется между этими задачами.
Распределением времени центрального процессора занимается специальный модуль операционной системы - планировщик. Планировщик по очереди передает управление отдельным задачам, так что даже в однопроцессорной системе создается полная иллюзия параллельной работы запущенных задач.
Распределение времени выполняется по прерываниям системного таймера. Поэтому каждой задаче дается определенный интервал времени, в течении которого она находится в активном состоянии.
Заметим, что распределение времени выполняется для задач, а не для процессов. Задачи, созданные разными процессами, конкурируют между собой за получение процессорного времени.
Каким именно образом?
Приложения Java могут указывать три значения для приоритетов задач. Это NORM_PRIORITY, MAX_PRIORITY и MIN_PRIORITY.
По умолчанию вновь созданная задача имеет нормальный приоритет NORM_PRIORITY. Если остальные задачи в системе имеют тот же самый приоритет, то все задачи пользуются процессорным времени на равных правах.
При необходимости вы можете повысить или понизить приоритет отдельных задач, определив для них значение приоритета, соответственно, MAX_PRIORITY или MIN_PRIORITY. Задачи с повышенным приоритетом выполняются в первую очередь, а с пониженным - только при отсутствии готовых к выполнению задач, имеющих нормальный или повышенный приоритет.
Процесс
Процесс (process) - это объект, который создается операционной системой, когда пользователь запускает приложение. Процессу выделяется отдельное адресное пространство, причем это пространство физически недоступно для других процессов. Процесс может работать с файлами или с каналами связи локальной или глобальной сети. Когда вы запускаете текстовый процессор Microsoft Word for Windows или программу калькулятора, вы создаете новый процесс.
Процессы, задачи и приоритеты
Прежде чем приступить к разговору о мультизадачности, следует уточнить некоторые термины.
Обычно в любой мультизадачной операционной системе выделяют такие объекты, как процессы и задачи. Между ними существует большая разница, которую следует четко себе представлять.
Производные от класса InputStream
От класса InputStream производится много других классов, как это показано на рис. 2.2.
Рис. 2.2. Классы, производные от класса InputStream
Производные от класса OutputStream
Класс OutputStream предназначен для создания потоков вывода. Приложения, как правило, непосредственно не используют этот класс для операций вывода, так же как и класс InputStream для операций ввода. Вместо этого применяются классы, иерархия которых показана на рис. 2.3.
Рис. 2.3. Классы, производные от класса OutputtStream
Рассмотрим кратко назначение этих классов.
Произвольный доступ к файлам
В ряде случаев, например, при создании системы управления базой данных, требуется обеспечить произвольный доступ к файлу. Рассмотренные нами ранее потоки ввода и вывода пригодны лишь для последовательного доступа, так как в соответствующих классах нет средств позиционирования внутри файла.
Между тем библиотека классов Java содержит класс RandomAccessFile, который предназначен специально для организации прямого доступа к файлам как для чтения, так и для записи.
В классе RandomAccessFile определено два конструктора, прототипы которых показаны ниже:
public RandomAccessFile(String name, String mode);
public RandomAccessFile(File file, String mode);
Первый из них позволяет указывать имя файла, и режим mode, в котором открывается файл. Второй конструктор вместо имени предполагает использование объекта класса File.
Если файл открывается только для чтения, вы должны передать конструктору текстовую строку режима "r". Если же файл открывается и для чтения, и для записи, конструктору передается строка "rw".
Позиционирование внутри файла обеспечивается методом seek, в качестве параметра pos которому передается абсолютное смещение файла:
public void seek(long pos);
После вызова этого метода текущая позиция в файле устанавливается в соответствии со значением параметра pos.
В любой момент времени вы можете определить текущую позицию внутри файла, вызвав метод getFilePointer:
public long getFilePointer();
Еще один метод, который имеет отношение к позиционированию, называется skipBytes:
public int skipBytes(int n);
Он работает так же, как и одноименный метод для потоков - продвигает текущую позицию в файле на заданное количество байт.
С помощью метода close вы должны закрывать файл, после того как работа с им завершена:
public void close();
Метод getFD позволяет получить дескриптор файла:
public final FileDescriptor getFD();
С помощью метода length вы можете определить текущую длину файла:
public long length();
Ряд методов предназначен для выполнения как обычного, так и форматированного ввода из файла. Этот набор аналогичен методам, определенным для потоков:
public int read();
public int read(byte b[]);
public int read(byte b[], int off, int len);
public final boolean readBoolean();
public final byte readByte();
public final char readChar();
public final double readDouble();
public final float readFloat();
public final void readFully(byte b[]);
public final void readFully(byte b[], int off, int len);
public final int readInt();
public final String readLine();
public final long readLong();
public final short readShort();
public final int readUnsignedByte();
public final int readUnsignedShort();
public final String readUTF();
Существуют также методы, позволяющие выполнять обычную или форматированную запись в файл с прямым доступом:
public void write(byte b[]);
public void write(byte b[], int off, int len);
public void write(int b);
public final void writeBoolean(boolean v);
public final void writeByte(int v);
public final void writeBytes(String s);
public final void writeChar(int v);
public final void writeChars(String s);
public final void writeDouble(double v);
public final void writeFloat(float v);
public final void writeInt(int v);
public final void writeLong(long v);
public final void writeShort(int v);
public final void writeUTF(String str);
Имена приведенных методов говорят сами за себя, поэтому мы не будем их описывать.
Просмотр списка аплетов
Список класса Enumeration можно просмотреть в цикле только один раз, вызывая для получения очередного элемента списка метод nextElement:
while(eApplets.hasMoreElements())
{
Applet currentApplet = (Applet)(eApplets.nextElement());
. . .
}
Для проверки условия завершения цикла следует вызывать метод hasMoreElements, который возвращает значение true, если в процессе просмотра список еще не был опустошен.
Заметим, что в полученном списке будет ссылка и на тот аплет, который выполняет поиск остальных аплетов. При необходимости в процессе просмотра вы можете выделить “свой” аплет следующим образом:
if(currentApplet == this)
{
// Обработка “своего” аплета
}
Простейшие методы
Создав выходной поток на базе класса FileOutputStream, вы можете использовать для записи в него данных три разновидности метода write, прототипы которых представлены ниже:
public void write(byte b[]);
public void write(byte b[], int off, int len);
public void write(int b);
Первый из этих методов записывает в поток содержимое массива, ссылка на который передается через параметр, начиная с текущей позиции в потоке. После выполнения записи текущая позиция продвигается вперед на число записанных байт, которое при успешном завершении операции равно длине массива (b.length).
Второй метод позволяет дополнительно указать начальное смещение off записываемого блока данных в массиве и количество записываемых байт len.
Третий метод просто записывает в поток один байт данных.
Если в процессе записи происходит ошибка, возникает исключение IOException.
Для входного потока, созданного на базе класса FileInputStream, определены три разновидности метода read, выполняющего чтение данных:
public int read();
public int read(byte b[]);
public int read(byte b[], int off, int len);
Первая разновидность просто читает из потока один байт данных. Если достигнут конец файла, возвращается значение -1.
Вторая разновидность метода read читает данные в массив, причем количество прочитанных данных определяется размером массива. Метод возвращает количество прочитанных байт данных или значение -1, если в процессе чтения был достигнут конец файла.
И, наконец, третий метод позволяет прочитать данные в область массива, заданную своим смещением и длиной.
Если при чтении происходит ошибка, возникает исключение IOException.
Проверка существования файла или каталога
С помощью метода exists вы можете проверить существование файла или катлога, для которого был создан объект класса File:
public boolean exists();
Этот метод можно применять перед созданием потока на базе класса FileOutputStream, если вам нужно избежать случайной перезаписи существующего файла. В этом случае перед созданием выходного потока класса FileOutputStream следует создать объект класса File, указав конструктору путь к файлу, а затем проверить сущестование файла методом exists.
Проверка возможности чтения и записи
Методы canRead и canWrite позволяют проверить возможность чтения из файла и записи в файл, соответственно:
public boolean canRead();
public boolean canWrite();
Их полезно применять перед созданием соответствующих потоков, если нужно избежать возникновение исключений, связанных с попыткой выполнения доступа неразрешенного типа. Если доступ разрешен, эти методы возвращают значение true, а если запрещен - false.
Работа с файлами и каталогами при помощи класса File
В предыдущих разделах мы рассмотрели классы, предназначенные для чтения и записи потоков. Однако часто возникает необходимость выполнения и таких операций, как определение атрибутов файла, создание или удаление каталогов, удаление файлов, получение списка всех файлов в каталоге и так далее. Для выполнения всех этих операций в приложениях Java используется класс с именем File.
Работа с потоковыми сокетами
Как мы уже говорили, интерфейс сокетов позволяет передавать данные между двумя приложениями, работающими на одном или разных узлах сети. В процессе создания канала передачи данных одно из этих приложений выполняет роль сервера, а другое - роль клиента. После того как канал будет создан, приложения становятся равноправными - они могут передавать друг другу данные симметричным образом.
Рассмотрим этот процесс в деталях.
Работа со стандартными потоками
Приложению Java доступны три стандратных потока, которые всегда открыты: стандартный поток ввода, стандартный поток вывода и стандартный поток вывода сообщений об ошибках.
Все перечисленные выше потоки определены в классе System как статические поля с именами, соответственно, in, out и err:
public final class java.lang.System
extends java.lang.Object
{
public static PrintStream err;
public static InputStream in;
public static PrintStream out;
. . .
}
Заметим, что стандратные потоки, как правило, не используются аплетами, так как навигаторы Internet общаются с пользователем через окно аплета и извещения от мыши и клавиатуры, а не через консоль.
Реализация интерфейса Runnable
Описанный выше способ создания задач как объектов класса Thread или унаследованных от него классов кажется достаточнао естественным. Однако этот способ не единственный. Если вам нужно создать только одну задачу, работающую одновременно с кодом аплета, проще выбрать второй способ с использованием интерфейса Runnable.
Идея заключается в том, что основной класс аплета, который является дочерним по отношению к классу Applet, дополнительно реализует интерфейс Runnable, как это показано ниже:
public class MultiTask extends Applet implements Runnable
{
Thread m_MultiTask = null;
. . .
public void run()
{
. . .
}
public void start()
{
if (m_MultiTask == null)
{
m_MultiTask = new Thread(this);
m_MultiTask.start();
}
}
public void stop()
{
if (m_MultiTask != null)
{
m_MultiTask.stop();
m_MultiTask = null;
}
}
}
Внутри класса необходимо определить метод run, который будет выполняться в рамках отдельной задачи. При этом можно считать, что код аплета и код метода run работают одновременно как разные задачи.
Для создания задачи используется оператор new. Задача создается как объект класса Thread, причем конструктору передается ссылка на класс аплета:
m_MultiTask = new Thread(this);
При этом при запуске задачи управление получит метод run, определенный в классе аплета.
Как запустить задачу?
Запуск выполняется, как и раньше, методом start. Обычно задача запускается из метода start аплета, когда пользователь отображает страницу сервера Web, содержащую аплет. Остановка задачи выполняется методом stop.
Реализация мультизадачности в Java
Для создания мультизадачных приложений Java вы должны воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания задач, управления их состоянием и синхронизации.
Как пользоваться классом Thread?
Есть две возможности.
Во-первых, вы можете создать свой дочерний класс на базе класса Thread. При этом вы должны переопределить метод run. Ваша реализация этого метода будет работать в рамках отдельной задачи.
Во-вторых, ваш класс может реализовать интерфейс Runnable. При этом в рамках вашего класса необходимо определить метод run, который будет работать как отдельная задача.
Как вы скоро увидите, система автоматизированного создания приложений Java, входящая в состав Microsoft Visual J++, пользуется вторым из перечисленных выше способов. Этот способ удобен в тех случаях, когда ваш класс должен быть унаследован от какого-либо другого класса (например, от класса Applet) и при этом вам нужна мультизадачность. Так как в языке программирования Java нет множественного наследования, невозможно создать класс, для которого в качестве родительского будут выступать классы Applet и Thread. В этом случае реализация интерфейса Runnable является единственным способом решения задачи.
Синхронизация методов
Возможность синхронизации как бы встроена в каждый объект, создаваемый приложением Java. Для этого объекты снабжаются защелками, которые могут быть использованы для блокировки задач, обращающихся к этим объектам.
Чтобы воспользоваться защелками, вы можете объявить соответствующий метод как synchronized, сделав его синхронизированным:
public synchronized void decrement()
{
. . .
}
При вызове синхронизированного метода соответствующий ему объект (в котором он определен) блокируется для использования другими синхронизированными методами. В результате предотвращается одновременная запись двумя методами значений в область памяти, принадлежащую данному объекту.
Использование синхронизированных методов - достаточно простой способ синхронизации задач, обращающихся к общим критическим ресурсам, наподобие описанного выше банковского счета.
Заметим, что не обязательно синхронизовать весь метод - можно выполнить синхронизацию только критичного фрагмента кода.
. . .
synchronized(Account)
{
if(Account.check(3000000))
Account.decrement(3000000);
}
. . .
Здесь синхронизация выполняется для объекта Account.
Синхронизация задач
Мультизадачный режим работы открывает новые возможности для программистов, однако за эти возможности приходится расплачиваться усложнением процесса проектирования приложения и отладки. Основная трудность, с которой сталкиваются программисты, никогда не создававшие ранее мультизадачные приложения, это синхронизация одновременно работающих задач.
Для чего и когда она нужна?
Однозадачная программа, такая, например, как программа MS-DOS, при запуске получает в монопольное распоряжение все ресурсы компьютера. Так как в однозадачной системе существует только один процесс, он использует эти ресурсы в той последовательности, которая соответствует логике работы программы. Процессы и задачи, работающие одновременно в мультизадачной системе, могут пытаться обращаться одновременно к одним и тем же ресурсам, что может привести к неправильной работе приложений.
Поясним это на простом примере.
Пусть мы создаем программу, выполняющую операции с банковским счетом. Операция снятия некоторой суммы денег со счета может происходить в следующей последовательности:на первом шаге проверяется общая сумма денег, которая хранится на счете;
если общая сумма равна или превышает размер снимаемой суммы денег, общая сумма уменьшается на необходимую величину;
значение остатка записывается на текущий счет.
Если операция уменьшения текущего счета выполняется в однозадачной системе, то никаких проблем не возникнет. Однако представим себе, что два процесса пытаются одновременно выполнить только что описанную операцию с одним и тем же счетом. Пусть при этом на счету находится 5 млн. долларов, а оба процесса пытаются снять с него по 3 млн. долларов.
Допустим, события разворачиваются следующим образом:первый процесс проверяет состояние текущего счета и убеждается, что на нем хранится 5 млн. долларов;
второй процесс проверяет состояние текущего счета и также убеждается, что на нем хранится 5 млн. долларов;
первый процесс уменьшает счет на 3 млн. долларов и записывает остаток (2 млн. долларов) на текущий счет;
второй процесс выполняет ту же самую операцию, так как после проверки считает, что на счету по-прежнему хранится 5 млн. долларов.
В результате получилось, что со счета, на котором находилось 5 млн. долларов, было снято 6 млн. долларов, и при этом там осталось еще 2 млн. долларов! Итого - банку нанесен ущерб в 3 млн. долларов.
Как же составить программу уменьшения счета, чтобы она не позволяла вытворять подобное?
Очень просто - на время выполнения операций над счетом одним процессом необходимо запретить доступ к этому счету со стороны других процессов. В этом случае сценарий работы программы должен быть следующим:процесс блокирует счет для выполнения операций другими процессами, получая его в монопольное владение;
процесс проводит процедуру уменьшения счета и записывает на текущий счет новое значение остатка;
процесс разблокирует счет, разрешая другим процессам выполнение операций.
Когда первый процесс блокирует счет, он становится недоступен другим процессам. Если второй процесс также попытается заблокировать этот же счет, он будет переведен в состояние ожидания. Когда первый процесс уменьшит счет и на нем останется 2 млн. долларов, второй процесс будет разблокирован. Он проверит остаток, убедится, что сумма недостаточна и не будет проводить операцию.
Таким образом, в мультизадачной среде необходима синхронизация задач при обращении к критическим ресурсам. Если над такими ресурсами будут выполняться операции в неправильной последовательности, это приведет к возникновению трудно обнаруживаемых ошибок.
В языке программирования Java предусмотрено несколько средств для синхронизации задач, которые мы сейчас рассмотрим.
Создание дочернего класса на базе класса Thread
Рассмотрим первый способ реализации мультизадачности, основанный на наследовании от класса Thread. При использовании этого способа вы определяете для задачи отдельный класс, например, так:
class DrawRectangles extends Thread
{
. . .
public void run()
{
. . .
}
}
Здесь определен класс DrawRectangles, который является дочерним по отношению к классу Thread.
Обратите внимание на метод run. Создавая свой класс на базе класса Thread, вы должны всегда определять этот метод, который и будет выполняться в рамках отдельной задачи.
Заметим, что метод run не вызывается напрямую никакими другими методами. Он получает управление при запуске задачи методом start.
Как это происходит?
Рассмотрим процедуру запуска задачи на примере класса DrawRectangles.
Вначале ваше приложение должно создать объект класса Thread:
public class MultiTask2 extends Applet
{
Thread m_DrawRectThread = null;
. . .
public void start()
{
if (m_DrawRectThread == null)
{
m_DrawRectThread = new DrawRectangles(this);
m_DrawRectThread.start();
}
}
}
Создание объекта выполняется оператором new в методе start, который получает управление, когда пользователь открывает документ HTML с аплетом. Сразу после создания задача запускается на выполнение, для чего вызывается метод start.
Что касается метода run, то если задача используется для выполнения какой либо периодической работы, то этот метод содержит внутри себя бесконечный цикл. Когда этот цикл завершается и метод run возвращает управление, задача прекращает свою работу нормальным, не аварийным образом. Для аварийного завершения задачи можно использовать метод interrupt.
Остановка работающей задачи выполняется методом stop. Обычно остановка всех работающих задач, созданных аплетом, выполняется методом stop класса аплета:
public void stop()
{
if (m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
}
Напомним, что этот метод вызывается, когда пользователь покидает страницу сервера Web, содержащую аплет.
Создание каталогов
С помощью методов mkdir и mkdirs можно создавать новые каталоги:
public boolean mkdir();
public boolean mkdirs();
Первый из этих методов создает один каталог, второй - все подкаталоги, ведущие к создаваемому каталогу (то есть полный путь).
Создание объекта класса File
У вас есть три возможности создать объект класса File, вызвав для этого один из трех конструкторов:
public File(String path);
public File(File dir, String name);
public File(String path, String name);
Первый из этих конструкторов имеет единственный параметр - ссылку на строку пути к файлу или каталогу. С помощью второго конструктора вы можете указать отдельно каталог dir и имя файла, для которого создается объект в текущем каталоге. И, наконец, третий конструктор позволяет указать полный путь к каталогу и имя файла.
Если первому из перечисленных конструкторов передать ссылку со значением null, возникнет исключение NullPointerException.
Пользоваться конструкторам очень просто. Вот, например, как создать объект класса File для файла c:\autoexec.bat и каталога d:\winnt:
f1 = new File(“c:\\autoexec.bat”);
f2 = new File(“d:\\winnt”);
Создание объекта класса InetAddress для локального узла
Метод getLocalHost создает объект класса InetAddress для локального узла, то есть для той рабочей станции, на которой выполняется приложение Java. Так как этот метод статический, вы можете вызывать его, ссылаясь на имя класса InetAddress:
InetAddress iaLocal;
iaLocal = InetAddress.getLocalHost();
Создание объекта класса InetAddress для удаленного узла
В том случае, если вас интересует удаленный узел сети Internet или корпоративной сети Intranet, вы можете создать для него объект класса InetAddress с помощью методов getByName или getAllByName. Первый из них возвращает адрес узла, а второй - массив всех адресов IP, связанных с данным узлом. Если узел с указанным именем не существует, при выполнении методов getByName и getAllByName возникает исключение UnknownHostException.
Заметим, что методам getByName и getAllByName можно передавать не только имя узла, такое как “microsoft.com”, но и строку адреса IP в виде четырех десятичных чисел, разделенных точками.
После создания объекта класса InetAddress для локального или удаленного узла вы можете использовать другие методы этого класса.
Создание объекта класса MediaTracker
Объект класса MediaTracker создается следующим образом:
MediaTracker mt;
mt = new MediaTracker(this);
Конструктору класса MediaTracker передается ссылка на компонент, для которого необходимо отслеживать загрузку изображений. В данном случае это наш аплет, поэтому мы передаем конструктору значение this.
Создание потока для форматированного обмена данными
Оказывается, создание потоков, связанных с файлами и предназначенных для форматированного ввода или вывода, необходимо выполнять в несколько приемов. При этом вначале необходимо создать потоки на базе класса FileOutputStream или FileInputStream, а затем передать ссылку на созданный поток констркутору класса DataOutputStream или DataInputStream.
В классах FileOutputStream и FileInputStream предусмотрены конструкторы, которым в качестве параметра передается либо ссылка на объект класса File, либо ссылка на объект класса FileDescriptor, либо, наконец, текстовая строка пути к файлу:
public FileOutputStream(File file);
public FileOutputStream(FileDescriptor fdObj);
public FileOutputStream(String name);
Таким образом, если вам нужен выходной поток для записи форматированных данных, вначале вы создаете поток как объект класса FileOutputStream. Затем ссылку на этот объект следует передать конструктору класса DataOutputStream. Полученный таким образом объект класса DataOutputStream можно использовать как выходной поток, записывая в него форматированные данные.
Создание потоков, связанных с файлами
Если вам нужно создать входной или выходной поток, связанный с локальным файлом, следует воспользоваться классами из библиотеки Java, созданными на базе классов InputStream и OutputStream. Мы уже кратко рассказывали об этих классах в разделе “Классы Java для работы с потоками”. Однако методика использования перечисленных в этом разделе классов может показаться довольно странной.
В чем эта странность?
Говоря кратко, странность заключается в том, что для создания потока вам необходимо воспользоваться сразу несколькими классами, а не одним, наиболее подходящим для решения поставленной задачи, как это можно было бы предположить.
Поясним сказанное на примере.
Пусть, например, нам нужен выходной поток для записи форматированных данных (скажем, текстовых строк класса String). Казалось бы, достаточно создать объект класса DataOutputStream, - и дело сделано. Однако не все так просто.
В классе DataOutputStream предусмотрен только один конструктор, которому в качестве параметра необходимо передать ссылку на объект класса OutputStream:
public DataOutputStream(OutputStream out);
Что же касается конструктора класса OutputStream, то он выглядит следующим образом:
public OutputStream();
Так как ни в том, ни в другом конструкторе не предусмотрено никаких ссылок на файлы, то непонятно, как с использованием только одних классов OutputStream и DataOutputStream можно создать выходной поток, связанный с файлом.
Что же делать?
Сравнение адресов IP
И, наконец, метод equals предназначен для сравнения адресов IP как объектов класса InetAddress.
Сравнение объектов класса File
Для сравнения объектов класса File вы должны использовать метод equals:
public boolean equals(Object obj);
Заметим, что этот метод сравнивает пути к файлам и каталогам, но не сами файли или каталоги.
Стандартные потоки
Для работы со стандартными потоками в классе System имеется три статических объекта: System.in, System.out и System.err. По своему назначению эти потоки больше всего напоминают стандартные потоки ввода, вывода и вывода сообщений об ошибках операционной системы MS-DOS.
Поток System.in связан с клавиатурой, поток System.out и System.err - с консолью приложения Java.
Стандартный поток ввода
Стандартный поток ввода in определен как статический объект класса InputStream, который содержит только простейшие методы для ввода данных. Нужнее всего вам будет метод read:
public int read(byte b[]);
Этот метод читает данные из потока в массив, ссылка на который передается через единственный параметр. Количество считанных данных определяется размером массива, то есть значением b.length.
Метод read возвращает количество прочитанных байт данных или -1, если достигнут конец потока. При возникновении ошибок создается исключение IOException, обработку которого необходимо предусмотреть.
Стандартный поток вывода
Стандартный поток вывода out создан на базе класса PrintStream, предназначенного, как мы это отмечали раньше, для форматированного вывода данных различного типа с целью их визуального отображения в виде текстовой строки.
Для работы со стандартным потоком вывода вы будете использовать главным образом методы print и println, хотя метод write также доступен.
В классе PrintStream определено несколько реализаций метода print с параметрами различных типов:
public void print(boolean b);
public void print(char c);
public void print(char s[]);
public void print(double d);
public void print(float f);
public void print(int i);
public void print(long l);
public void print(Object obj);
public void print(String s);
Как видите, вы можете записать в стандартный поток вывода текстовое представление данных различного типа, в том числе и класса Object.
Метод println аналогичен методу print, отличаясь лишь тем, что он добавляет к записываемой в поток строке символ перехода на следующую строку:
public void println();
public void println(boolean b);
public void println(char c);
public void println(char s[]);
public void println(double d);
public void println(float f);
public void println(int i);
public void println(long l);
public void println(Object obj);
public void println(String s);
Реализация метода println без параметров записывает только символ перехода на следующую строку.
Стандртный поток вывода сообщений об ошибках
Стандртный поток вывода сообщений об ошибках err так же, как и стадартный поток вывода out, создан на базе класса PrintStream. Поэтому для записи сообщений об ошибках вы можете использовать только что описанные методы print и println.
Структура комбинированных приложений
Рассмотрим структуру комбинированного приложения Combi, полные исходные тексты которого мы привели ниже в этой главе.
Связь приложений Java с расширениями сервера Web
Итак, мы расказали вам, как приложения Java могут получать с сервера Web для обработки произвольные файлы, а также как они могут передавать данные друг другу с применением потоковых или датаграммных сокетов.
Однако наиболее впечатляющие возможности открываются, если организовать взаимодействие между приложением Java и расширением сервера Web, таким как CGI или ISAPI. В этом случае приложения или аплеты Java могли бы посылать произвольные данные расширению сервера Web для обработки, а затем получать результат этой обработки в виде файла.
О том, как сделать расширение сервера Web с применением интерфейса CGI или ISAPI вы можете узнать из 29 тома “Библиотеки системного программиста”, который называется “Сервер Web своими руками”. Если вы никогда раньше не создавали расширений сервера Web, мы настоятельно рекомендуем вам ознакомиться с этой книгой перед тем как продолжить работу над данным разделом.
Удаление файлов и каталогов
Для удаления ненужного файла или каталога вы должны создать соответствующий объект File и затем вызвать метод delete:
public boolean delete();
Универсальный адрес ресурсов URL
Адрес IP позволяет идентифицировать узел, однако его недостаточно для идентификации ресурсов, имеющихся на этом узле, таких как работающие приложения или файлы. Причина очевидна - на узле, имеющем один адрес IP, может существовать много различных ресурсов.
Для ссылки на ресурсы сети Internet применяется так называемый универсальный адрес ресуросв URL (Universal Resource Locator). В общем виде этот адрес выглядит следующим образом:
[protocol]://host[:port][path]
Строка адреса начинаетс с протокола protocol, который должен быть использован для доступа к ресурсу. Документы HTML, например, передаются из сервера Web удаленным пользователям с помощью протокола HTTP. Файловые серверы в сети Internet работают с протоколом FTP.
Для ссылки на сетевые ресурсы через протокол HTTP используется следующая форма универсального адреса ресурсов URL:
http://host[:port][path]
Параметр host обязательный. Он должен быть указан как доменный адрес или как адрес IP (в виде четырех десятичных чисел). Например:
http://www.microsoft.com
http://154.23.12.101
Необязательный параметр port задает номер порта для работы с сервером. По умолчанию для протокола HTTP используется порт с номером 80, однако для специализированных серверов Web это может быть и не так.
Номер порта идентифицирует программу, работающую в узле сети TCP/IP и взаимодействующую с другими программами, расположенными на том же или на другом узле сети. Если вы разрабатываете программу, передающую данные через сеть TCP/IP с использованием, например, интерфейса сокетов, то при создании канала связи с уделенным компьютером вы должны указать не только адрес IP, но и номер порта, который будет использован для передачи данных.
Ниже мы показали, как нужно указывать в адресе URL номер порта:
http://www.myspecial.srv/:82
Теперь займемся параметром path, определяющем путь к объекту.
Обычно любой сервер Web или FTP имеет корневой каталог, в котором расположены подкаталоги. Как в корневом каталоге, так и в подкаталогах сервера Web могут находиться документы HTML, двоичные файлы, файлы с графическими изображениями, звуковые и видео-файлы, расширения сервера в виде программ CGI или библиотек динамической компоновки, дополняющих возможности сервера (такие, как библиотеки ISAPI для сервера Microsoft Information Server).
Если в качестве адреса URL указать навигатору только доменное имя сервера, сервер перешлет навигатору свою главную страницу. Имя файла этой страницы зависит от сервера. Большинство серверов на базе операционной системы UNIX посылают по умолчанию файл документа с именем index.html. Сервер Microsoft Information Server может использовать для этой цели имя default.htm или любое другое, определенное при установке сервера, например, home.html или home.htm.
Для ссылки на конкретный документ HTML или на файл любого другого объекта необходимо указать в адресе URL его путь, включающий имя файла, например:
http://www.glasnet.ru/~frolov/index.html
http://www.dials.ccas.ru/frolov/bin/dbsp26.lzh
Корневой каталог сервера Web обозначается символом /. В спецификации протокола HTTP сказано, что если путь не задан, то используется корневой каталог.
Видео в окне аплета
Наиболее динамичные страницы сервера Web содержат анимационные изображения в виде небольших видеофильмов. Как мы рассказывали в 29 томе “Библиотеки системного программиста”, который называется “Сервер Web своими руками”, вы можете подготовить видеофильм как файл AVI или как многосекционный файл GIF.
Файл AVI представляет собой многопоточный файл, содержащий видео и звук. О том, как создавать такие файлы, мы рассказали в 15 томе “Библиотеки системного программиста” с называнием “Мультимедиа для Windows”. Файлы AVI можно создавать при помощи специального видеоадаптера, который способен оцифровывать сигнал с видеокамеры или видеомагнитофона, а также из отдельных изображений, составляющих кадры видеофильма.
Заметим, однако, что озвученный видеофильм в формате AVI продолжительностью в 1 минуту занимает мегабайты дискового пространства. При существующих на сегодняшний день скоростях передачи данных через Internet не имеет никакого смысла размещать на страницах сервера Web такие файлы.
Многосекционные файлы GIF не содержат звуковой информации и состоят обычно из одного-двух десятков кадров. Для каждого такого кадра вы можете задавать время отображения и координаты, где этот кадр будет отображаться. Можно также добиться зацикленного отображения видеофильма, созданного как многосекционный файл GIF.
Аплеты Java предоставляют вам еще одну возможность отображения небольших видеофильмов на страницах сервера Web.
Для реализации этой возможности вы должны подготовить и разместить в одном из каталогов сервера Web файлы отдельных кадров видеофильма в формате GIF или JPEG.
Аплет Java должен загрузить эти изображения, дождавшись окончания процесса загрузки, что можно сделать либо при помощи рассмотренного в этой главе класса MediaTracker либо при помощи интерфейса ImageObserver.
Как только все изображения будут полностью загружены, аплет может начинать их поочередное отображение в цикле. Этот цикл должен выполняться в отдельной задаче.
Так как аплет полностью контролирует отображение кадров фильма, он может реализовывать эффекты, недостижимые при использовании файлов AVI или многосекционных файлов GIF. Например, аплет может накладывать или смешивать кадры различных фильмов, рисовать поверх кадров произвольные изображения или делать надписи, масштабировать отдельные фрагменты кадров или весь кадр и так далее. Здесь все ограничивается главным образом вашей фантазией.
Так как мы уже научились выполнять все необходимые для показа видеофильма операции, перейдем сразу к исходным текстам приложения CDRotation.
Временная приостановка и возобновление работы
Методы suspend и resume позволяют, соответственно, временно приостанавливать и возобновлять работу задачи. Мы уже пользовались этими методами в приложении Rectangles для приостановки и возобновления работы задачи рисования прямоугольников.
Задача приостанавливалась, когда курсор мыши оказывался над окном аплета:
public boolean mouseEnter(Event evt, int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.suspend();
}
return true;
}
Работа задачи возобновлялась, когда курсор мыши покидал окно аплета:
public boolean mouseExit(Event evt, int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.resume();
}
return true;
}
Создание приложений на языке Java.
В 30 томе “Библиотеки системного программиста”, который называется “Microsoft Visual J++. Создание приложений на языке Java. Часть 1” мы научили вас создавать аплеты Java и размещать их на страницах сервера Web. Была рассмотрена общая структура аплета, работа с контекстом отображения, органами управления, панелями, шрифтами и многое другое. Теперь настало время поговорить о более сложных и весьма полезных возможностях, которые открываются перед разработчиком приложений Java.
Прежде всего, это мультизадачность. Практически любая современная операционная система, такая как Microsoft Windows, IBM OS/2 или UNIX, работает в мультизадачном режиме. Во многих случаях мультизадачность в целом благоприятно сказывается на производительности системы, так как во время ожидания одних задач свою работу могут выполнять другие задачи, готовые для этого.
Например, если вы работаете в сети Internet, то можете одновременно подключиться к нескольким серверам FTP и Web, перекачивая сразу несколько файлов и загружая несколько документов HTML. При этом еще можно отправлять или получать электронную почту. Так как скорость поступления данных из сети Internet составляет в среднем 1 Кбайт в секунду, то даже при использовании модемного соединения общая скорость передачи данных в этом случае будет выше, чем при поочередной работе с серверами FTP, Web или с почтовым сервером. Пока один из серверов находится в состоянии ожидания, вы будете получать данные от другого сервера.
Такое увеличение средней скорости передачи данных возможно из-за того, что при использовании протокола TCP/IP через общий канал могут одновременно передаваться пакеты данных, предназначенные для различных адресатов.
Если вы создаете приложения Java, вам доступны удобные средства организации мультзадачности, в том числе средства синхронизации задач. Последнее необходимо для того чтобы параллельно работающие задачи корректно обращались с критическими ресурсами, требующими последовательного обращения.
Заметим, что если вы собираетесь заниматься анимацией в окнах аплетов, вам в любом случае придется создавать мультизадачные приложения Java. Использование мультизадачности - единственный путь выполнения в приложениях Java любых периодических процедур, таких, например, как покадровое отображение мультфильма или медленный сдвиг текста для создания эффекта “бегущая строка”.
Отдельная глава будет посвящена организации файлового ввода и вывода в приложениях Java. Хотя аплеты не имеют доступа к файлам, расположенным на дисках локального компьютера, самостоятельные приложения Java могут обращаться с файлами свободно. Аплеты также могут иметь доступ к файлам, расположенным в каталогах сервера Web.
Много внимания в нашей книге мы уделим организации сетевого взаимодействия аплетов. Язык программирования Java был разработан специально для создания сетевых приложений, поэтому не удивительно, что в состав его библиотеки классов входят мощные средства, предназначенные для работы в сети. Мы, в частности, рассмотрим применение интерфейса потоковых и датаграмных сокетов.
Вы сможете более полно реализовать возможности аплетов, если сумеете организовать взаимодействие аплетов и расширений сервера Web, таких как программы CGI или приложения ISAPI. В нашей книге вы найдете описание практических способов организации такого взаимодействия.
Традиционно работа с растровыми графическими изображениями в приложениях Microsoft Windows или IBM OS/2 вызывала у программистов трудности, связанные с необходимостью разбора заголовков файлов графических изображений, реализации палитры и так далее. Библиотека классов Java содержит очень удобные и простые в использовании средства, избавляющие программистов от кошмарной работы с графическими файлами на низком уровне. Это особенно важно, так как аплеты часто применяются именно для усиления графического оформления страниц серверов Web. В нашей книге мы рассмотрим основные приемы работы с графическими изображениями. Отдельный раздел будет посвящена созданию анимационных изображений.
Мы расскажем вам также и о том, как аплеты Java работают со звуковыми файлами. И хотя средства, предоставляемые библиотекой классов Java для, работы со звуком, нельзя назвать богатыми, вы все же сможете применить аплеты для озвучивания своих документов HTML.
В отдельной главе нами будут рассмотрены приемы организации взаимодействия между несколькими аплетами, расположенными в одном документе HTML. Заключительная глава нашей книги посвящена созданию комбинированных приложений Java, которые могут работать и как самостоятельные приложения, и как аплеты, встроенные в документы HTML.
Взаимодействие приложения Java и расширения сервера Web
Методика организации взаимодействия приложений Java и расширений сервера Web основана на применении классов URL и URLConnection.
Приложение Java, желающее работать с расширением сервера Web, создает объект класса URL для программы расширения (то есть для исполняемого модуля расширения CGI или библиотеки динамической компоновки DLL расширения ISAPI).
Далее приложение получает ссылку на канал передачи данных с этим расширением как объекта класса URLConnection. Затем, пользуясь методами getOutputStream и getInputStream из класса URLConnection, приложение создает с расширением сервера Web выходной и входной канал передачи данных.
Когда данные передаются приложением в выходной канал, созданный подобным образом, он попадает в стандартный поток ввода приложения CGI, как будто бы данные пришли методом POST из формы, определенной в документе HTML.
Обработав полученные данные, расширение CGI записывает их в свой стандартный выходной поток, после чего эти данные становятся доступны приложению Java через входной поток, открытый методом getInputStream класса URLConnection.
На рис. 3.5 показаны потоки данных для описанной выше схемы взаимодействия приложения Java и расширения сервреа Web с интерфейсом CGI.
Рис. 3.5. Взаимодействие приложения Java с расширением сервера Web на базе интерфейса CGI
Расширения ISAPI работают аналогично, однако они получают данные не из стандратного входного потока, а с помощью вызова специально предназначенной для этого функции интерфейса ISAPI. Вместо стандартного потока вывода также применяется специальная функция. Подробности вы можете узнать из 29 тома “Библиотеки системного программиста”.
Для каждого процесса операционная система
Для каждого процесса операционная система создает одну главную задачу (thread или task), которая является потоком выполняющихся по очереди команд центрального процессора. При необходимости главная задача может создавать другие задачи, пользуясь для этого программным интерфейсом операционной системы.
Все задачи, созданные процессом, выполняются в адресном пространстве этого процесса и имеют доступ к ресурсам процесса. Однако задача одного процесса не имеет никакого доступа к ресурсам задачи другого процесса, так как они работают в разных адресных пространствах. При необходимости организации взаимодействия между процессами или задачами, принадлежащими разным процессам, следует пользоваться системными средствами, специально предназначенными для этого.
Задачи-демоны
Вызвав для задачи метод setDaemon, вы превращаете обычную задачу в задачу-демон. Такая задача работает в фоновом режиме независимо от породившей ее задачи. Если задача-демон создает другие задачи, то они также станут получат статус задачи-демона.
Заметим, что метод setDaemon необходимо вызывать после создания задачи, но до момента ее запуска, то есть перед вызовом метода start.
С помощью метода isDaemon вы можете проверить, является задача демоном, или нет.
Загрузка и проигрывание звуковых файлов
Работа со звуковыми файлами во многом напоминает работу с растровыми графическими файлами. Вначале вы должны получить ссылку на интерфейс AudioClip, а затем, пользуясь его методами, вы сможете выполнять проигрывание содержимого этого файла.
Для получения интерфейса AudioClip вы должны воспользоваться одним из двух вариантов метода getAudioClip, определенных в классе Applet:
public AudioClip getAudioClip(URL url):
public AudioClip getAudioClip(URL url, String name);
Первый вариант метода предполагает указание адреса URL звукового файла через единственный параметр, второй допускает раздельное указание адреса URL каталога, содержащего файл, и имени файла.
В документации на метод getAudioClip сказано, что этот метод фактически не выполняет загрузку звуковых данных, а только возвращает ссылку на интерфейс AudioClip и немедленно возвращает управление. Загрузка звуковых данных выполняется методами, предназначенными для проигрывания файла.
Однако в книге “The Java Tutorial. Object-Oriented Programming for the Internet”, подготовленной специалистами группы JavaSoft, утверждается, что текущие реализации Java работают по другому: метод getAudioClip возвращает управление только после завершения загрузки звукового файла. Очевидно, вам не стоит полагаться на то, что так будет всегда. В тех случаях, когда нежелательно блокирование работы аплета на время загрузки звукового файла, загрузку и проигрывание следует выполнять в отдельной задаче.
Интерфейс AudioClip определен следующим образом:
public interface java.applet.AudioClip
{
public abstract void play();
public abstract void loop();
public abstract void stop();
}
Метод play запускает однократное проигрывание звукового файла, которое выполняется от начала файла и до его конца.
Метод loop запускает проигрывание звукового файла в цикле, которое будет продолжаться до тех пор, пока вы не остановите его, вызвав метод stop.
Метод stop, как нетрудно догадаться из его названия, останавливает проигрывание звукового файла, как однократное, так и выполняемое в цикле.
Параметры x и y задают координаты верхнего левого угла прямоугольной области, внутри которой будет нарисовано изображение. Эти параметры также задаются для любого варианта метода drawImage.
Параметр bgcolor задает цвет фона, на котором будет нарисовано изображение. Как мы говорили в 29 томе “Библиотеки системного программиста”, изображения GIF могут быть прозрачными. В этом случае цвет фона может иметь большое значение.
Если для рисования выбраны варианты метода drawImage с параметрами width и height, изображение будет нарисовано с масштабированием. При этом указанные параметры будут определять, соответственно, ширину и высоту изображения.
Параметр observer представляет собой ссылку на объект класса ImageObserver, который получит извещение при загрузке изображения. Обычно в качестве такого объекта используется сам аплет, поэтому данный параметр указывается как this.
Вот два примера использования метода drawImage:
g.drawImage(FloppyDiskImg, 25, 3, this);
g.drawImage(FloppyDiskImg, 25, 42, 200, 200, this);
В первой строке изображение FloppyDiskImg рисуется в точке с координатами (25, 3) без масштабирования, во второй - в точке с координатами (25, 42), причем высота и ширина нарисованного изображения будет равна 200 пикселам.
Метод drawImage запускает процесс загрузки и рисования изображения, а затем, не дожидаясь его завершения, возвращает управление. Так как загрузка файла изображения по сети может отнять немало времени, она выполняется асинхронно в отдельной задаче.
Загрузка и рисование растрового изображения
Загрузка растрового изображения из файла выполняется очень просто - с помощью метода getImage, определенного в классе Applet:
public Image getImage(URL url);
public Image getImage(URL url, String name);
Первый вариант метода предполагает использование только одного параметра - адреса URL файла графического изображения. Второй позволяет дополнительно указать относительное расположение файла изображения относительно адреса URL, например:
Image img;
img = getImage(“http://www.glasnet.ru/~frolov/pic”,
"cd.gif");
Если аплет желает загрузить изображение, расположенное в том же каталоге, что и он сам, это можно сделать следующим образом:
img = getImage(getCodeBase(), "cd.gif");
Метод getCodeBase, определенный в классе Applet, возвращает адрес URL аплета. Вместо него можно использовать метод getDocumentBase, который также определен в классе Applet и возвращает адрес URL документа HTML, содержащего аплет:
img = getImage(getDocumentBase(), "cd.gif");
В любом случае метод getImage создает объект класса Image.
Заметим, что на самом деле метод getImage вовсе не загружает изображение через сеть, как это можно было бы подумать. Он только создает объект класса Image. Реальная загрузка файла растрового изображения будет выполняться методом рисования drawImage, который определен в классе Graphics:
public abstract boolean
drawImage(Image img, int x, int y,
ImageObserver observer);
public abstract boolean
drawImage(Image img, int x, int y, Color bgcolor,
ImageObserver observer);
public abstract boolean
drawImage(Image img, int x, int y, int width, int height,
ImageObserver observer);
public abstract boolean
drawImage(Image img, int x, int y, int width, int height,
Color bgcolor, ImageObserver observer);
Как видите, существует четыре варианта этого метода.
В качестве первого параметра любому варианту метода передается ссылка на объект класса Image, полученный ранее с помощью метода getImage.
Закрывание потоков
Работая с файлами в среде MS-DOS или Microsoft Windows средствами языка программирования С вы должны были закрывать ненужные более файлы. Так как в системе интерпертации приложений Java есть процесс сборки мусора, возникает вопрос - выполняет ли он автоматическое закрывание потоков, с которыми приложение завершило работу?
Оказывается, процесс сборки мусора не делает ничего подобного!
Сборка мусора выполняется только для объектов, размещенных в оперативной памяти. Потоки вы должны закрывать явным образом, вызывая для этого метод close.
Запись данных в поток и чтение данных из потока
Для обмена данными с потоками можно использовать как простейшие методы write и read, так и методы, допускающие ввод или вывод форматированных данных. В зависимости от того, на базе какого класса создан поток, зависит набор доступных методов, предназначенных для чтения или записи данных.
Завершение работы сервера и клиента
После завершения передачи данных вы должны закрыть потоки, вызвав метод close:
is.close();
os.close();
Когда канал передачи данных больше не нужен, сервер и клиент должны закрыть сокет, вызвав метод close, определенный в классе Socket:
s.close();
Серверное приложение, кроме того, должно закрыть соединение, вызвав метод close для объекта класса ServerSocket:
ss.close();
Бизнес: Предпринимательство - Малый бизнес - Управление