Создание приложений Java. Об авторах

Java!

Оглавление

    Авторы о себе

  • Фролов Александр Вячеславович

    Я родился 23 мая 1962 г. в Москве. В 1985 г. закончил МИФИ (факультет автоматики и электроники).

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

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

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

    В 1991 году написал первый том серии "Библиотека системного программиста". С этого времени работа технического писателя стала для меня основной, отстранив на задний план все остальное. В 1994 году вышла первая книга серии "Персональный компьютер. Шаг за шагом". С тех пор в соавторстве со своим братом мы написали свыше 35 книг, посвященных компьютерной тематике.

    Начиная с 1996 года мы с братом решили попробовать свои силы в новой области и приступили к написанию статей для различных периодических изданий. Наши статьи вышли в журналах "Компьютерра", "Мир ПК", "Hard and Soft", в приложении "Hooked Up" к газете Moscow Times, а также в газете "Капитал".

    Начиная с 1995 года мы сотрудничаем с АО "ДиалогНаука", когда к нам обратились за помощью в редактировании документации по антивирусным продуктам.

    Мое семейное положение таково: я женат уже 14 лет. Жена Наташа работает в Институте космических исследований. Нашему сыну Вове 11 лет. Его увлечения - игра на виолончели и фортепьяно (он ходит в музыкальную школу). Любит компьютерные игры, мультфильмы и игрушечную железную дорогу.

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

  • Фролов Григорий Вячеславович

    Я родился 8 декабря 1970 г. в Москве. В 1985 г. поступил в физико-математическую Школу при МИФИ. Все время мечтал заниматься физикой, но в результате поступил в МИФИ на факультет кибернетики.

    В институте работал на 22 кафедре под руководством Ильинского Николая Ивановича. Именно благодаря ему я получил возможность изучать персональные компьютеры, так как в то время студентам были доступны только ЕС ЭВМ и "персональный компьютер" ДВК.

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

    На третьем курсе института я познакомился с Сергеем Гладковым, в то время работавшим в АО "Диалог-МИФИ". Он предложил мне заняться изучением Microsoft Windows версии 2.0 и вместе с ним написать книгу о программировании для этой операционной системы. Когда материал книги был почти готов, корпорация Microsoft выпустила очередную версию Windows 3.0. Мы внесли все необходимые дополнения, и в 1992 году наша книга вышла под названием "Программирование в Microsoft Windows".

    После этого я серьезно занялся написанием технической литературы по компьютерной тематике. В настоящее время в авторских сериях "Библиотека системного программиста" и "Персональный компьютер. Шаг за шагом" вместе со своим братом мы написали свыше 35 книг. Кроме того, мы подготовили десятки статей в различные периодические издания.

    Что касается семейного положения, то я женат уже более 5 лет. Моя жена Оля вместе со мной закончила МИФИ и сейчас работает системным программистом в фирме "Сат-Тел". Я благодарен ей за поддержку во всех моих делах. У нас есть сын Дима. В октябре ему исполнится четыре года.

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

Назад


Создание приложений Java

.

Об авторах

    Добро пожаловать в мир Java!

    Вперед

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

    Многочисленные примеры приложений и аплетов помогут вам в кратчайшие сроки освоиться в мире Java.

    Оглавление

    • 1. ВВЕДЕНИЕ В JAVA
      • Инструментарий разработчика
      • Мобильность Java
      • Базовые типы данных
      • Библиотеки классов Java
      • Указатели, которых нет
      • Массивы в Java
      • Сборка мусора
      • Особенности реализации классов в Java
    • 2. ПЕРВОЕ ПРИЛОЖЕНИЕ
      • Установка Java WorkShop
      • Запуск Java WorkShop
      • Исходный текст приложения HelloJava
    • 3. ПЕРВЫЙ АПЛЕТ
      • Создание проект аплета
      • Исходный текст аплета
      • Методы в классе HelloApplet
      • Исходный текст документа HTML
      • Изменяем исходный текст аплета
      • Метод paint
      • Метод getAppletInfo
    • 4. РИСОВАНИЕ В ОКНЕ АПЛЕТА
      • Контекст отображения
      • Установка атрибутов контекста отображения
      • Определение атрибутов контекста отображения
      • Рисование геометрических фигур
      • Аплет Draw
      • Исходные тексты аплета Draw
      • Проект для аплета Draw
    • 5. СОБЫТИЯ
      • Как обрабатываются события
      • События от мыши
      • Аплет LineDraw
      • Исходный текст аплета LineDraw
      • Описание исходного текста
    • 6. КОМПОНЕНТЫ
      • Кнопки
      • Переключатели
      • Списки класса Choice
      • Списки класа List
      • Текстовое поле класса Label
      • Текстовое поле класса TextField
      • Многострочное текстовое поле класса TextArea
      • Аплет FormDemo
    • 7. РАБОТА С СИСТЕМОЙ LAYOUT MANAGER
      • Режимы системы Layout Manager
      • Режим FlowLayout
      • Режим GridLayout
      • Режим BorderLayout
      • Режим CardLayout
      • Режим GridBagLayout
      • Аплет FormLayout
    • 8. РАБОТА С ПАНЕЛЯМИ
      • Создание панелей
      • Добавление панелей
      • Добавление компонент в панели
      • Рисование в окне панели
      • Создание нового класса на базе класса Panel
      • Аплет Options
      • Исходный текст аплета Options
      • Описание исходного текста аплета Options
    • 9. ОКНА И ДИАЛОГОВЫЕ ПАНЕЛИ
      • Окна класса Frame
      • Меню в окне класса Frame
      • Класс Menu
      • Класс MenuItem
      • Создание диалоговых панелей
      • Приложение MenuApp
      • Исходный текст приложения MenuApp
      • Описание исходного текста приложения MenuApp
    • 10. МНОГОПОТОЧНОСТЬ
      • Процессы, потоки и приоритеты
      • Реализация многопоточности в Java
      • Применение многопоточности для анимации
      • Синхронизация потоков
      • Потоки-демоны
      • Аплет Rectangles
      • Исходные тексты аплета Rectangles
      • Описание исходных текстов аплета Rectangles
    • 11. РАБОТА С ФАЙЛАМИ
      • Классы Java для работы с потоками
      • Стандартные потоки
      • Создание потоков, связанных с файлами
      • Запись данных в поток и чтение данных из потока
      • Закрывание потоков
      • Принудительный сброс буферов
      • Потоки в оперативной памяти
      • Класс Stream Tokenizer для разбора входных потоков
      • Класс String Tokenizer
      • Работа с файлами и каталогами при помощи класса File
      • Произвольный доступ к файлам
      • Приложение StreamToken
      • Приложение DirectFile
    • 12. СОЗДАНИЕ СЕТЕВЫХ ПРИЛОЖЕНИЙ
      • Адрес IP и класс InetAddress
      • Универсальный адрес ресурсов URL
      • Класс URL в библиотеке классов Java
      • Передача данных с использованием сокетов
      • Работа с потоковыми сокетами
      • Класс Socket
      • Использование датаграммных сокетов
      • Связь приложений Java с расширениями сервера Web
      • Аплет ShowChart
      • Приложения SocketServ и SocketClient
      • Аплет Form
    • 13. РАСТРОВЫЕ ИЗОБРАЖЕНИЯ И АНИМАЦИЯ
      • Загрузка и рисование растрового изображения
      • Класс Image
      • Ожидание загрузки изображений
      • Видео в окне аплета
      • Аплет CDRotation
    • 14. ЗВУК В АПЛЕТАХ JAVA
      • Загрузка и проигрывание звуковых файлов
      • Аплет PlayClip

    Вперед

Назад


Введение в Java

Sun Microsystems

Введение
Инструментарий
Мобильность
Типы данных
Классы Java
Указатели
Массивы
Сборка мусора
Реализация классов

    Введение в Java

    Назад Вперед

    Казалось бы, на сегодняшний день изобретены уже все языки программирования, какие только можно придумать. Но нет - появился еще один, с названием Java. Этот язык сумел получить весьма заметную известность за последние несколько лет, так как он ориентирован на самую популярную компьютерную среду - сеть Internet и серверы Web.

    Язык Java произошел от языка программирования Oak (а не от C++, как думают многие). Oak был приспособлен для работы в Internet и затем переименован в Java. Подробнее об этом вы можете узнать, прочитав статью по адресу http://www.sun.ru/java/start/intro/history.html.

    Изучая Java, вы будете приятно удивлены тем, что его синтаксис близок к синтаксису языка C++. Унаследовав самое лучшее от языка программирования C++, язык Java при этом избавился от некоторых недостатков C++, в результате чего на нем стало проще программировать. В этом языке нет, например, указателей, которые сложны в использовании и потенциально могут послужить причиной доступа программы к не принадлежащей ей области памяти. Нет множественного наследования и шаблонов, хотя функциональные возможности языка Java от этого не пострадали. Если вы умеете программировать на C++, для вас не составит особого труда изучить язык Java.

    Почему вам нужно изучать новый язык программирования Java?

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

    Огромное преимущество Java заключается в том, что на этом языке можно создавать приложения, способные работать на различных платформах. К сети Internet подключены компьютеры самых разных типов - Pentium PC, Macintosh, рабочие станции Sun и так далее. Даже в рамках компьютеров, созданных на базе процессоров Intel, существует несколько платформ, например, Microsoft Windows версии 3.1, Windows 95, Windows NT, OS/2, Solaris, различные разновидности операционной системы UNIX с графической оболочкой XWindows. Между тем, создавая сервер Web в сети Internet, вы бы наверняка хотели, чтобы им могло пользоваться как можно большее число людей. В этом случае вас выручат приложения Java, предназначенные для работы на различных платформах и не зависящие от конкретного типа процессора и операционной системы.

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

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

    Вторая группа - это так называемые аплеты (applets). Аплеты представляют собой разновидность приложений Java, которые интерпретируются виртуальной машиной Java, встроенной практически во все современные браузеры.

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

    Аплеты Java встраиваются в документы HTML, хранящиеся на сервере Web. С помощью аплетов вы можете сделать страницы сервера Web динамичными и интерактивными. Аплеты позволяют выполнять сложную локальную обработку данных, полученных от сервера Web или введенных пользователем с клавиатуры. Из соображений безопасности аплеты (в отличие от обычных приложений Java) не имеют никакого доступа к файловой системе локального компьютера. Все данные для обработки они могут получить только от сервера Web. Более сложную обработку данных можно выполнять, организовав взаимодействие между аплетами и расширениями сервера Web - приложениями CGI и ISAPI.

    Для повышения производительности приложений Java в современных браузерах используется компиляция "на лету"- Just-In-Time compilation (JIT). При первой загрузке аплета его код транслируется в обычную исполнимую программу, которая сохраняется на диске и запускается. В результате общая скорость выполнения аплета Java увеличивается в несколько раз.

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

    Назад Вперед

Назад


Инструментарий разработчика

Введение
Инструментарий
 Мобильность
Типы данных
Классы Java
Указатели
Массивы
Сборка мусора
Реализация классов

    Инструментарий разработчика

    Назад Вперед

    Первоначально средства разработки приложений и аплетов Java были созданы фирмой Sun Microsystems и до сих пор они пользуются популярностью. В сети Internet по адресу http://java.sun.com вы можете бесплатно получить набор Java Development Kit (JDK).

    Пакетные средства

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

    Интегрированные средства разработки

    Для тех, кто привык пользоваться визуальными средствами разработки, доступны два других инструмента, созданных в Sun Microsystems. Это Java WorkShop 2.0 и Java Studio 1.0.

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

    На рис. 1 мы показали внешний вид главного окна приложения Java WorkShop 2.0.

    pic01sm.gif (2791 bytes) Рис. 1. Главное окно приложения .

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

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

    Разработку элементов пользовательского интерфейса можно выполнять с помощью удобного средства визуального проектирования (рис. 2).

    pic02sm.gif (3283 bytes) Рис. 2. Java WorkShop 2.0 позволяет разрабатывать пользовательский интерфес с помощью визуальных средств.

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

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

    pic03sm.gif (2741 bytes) Рис. 3. Проектирование логики работы приложения.

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

    В наборе компонент JavaBeans, доступных в Java Studio, есть компоненты, предназначенные для работы с ресурсами Internet, базами данных и таблицами, формами и так далее.

    Проектирование пользовательского интерфейса в Java Studio, также как и в Java WorkShop, выполняется визуально (рис. 4).

    Рис. 4. Визуальное проектирование пользовательского интерфейса.

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

    pic04sm.gif (2356 bytes)

    Подборку различных инструментальных средств, предназначенных для создания приложений Java, вы найдете по адресу http://www.sun.ru/download/download.html.

    Браузеры

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

    Подключение к Internet

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

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

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

    Если вы собираетесь проверять работу приложений и аплетов Java, взаимодействующих с сервером Web, вы можете воспользоваться собственным сервером в Internet или в корпоративной сети Intranet (если они у вас есть). Можно также установить сервер Web, входящий в комплект операционной системы Windows NT Workstation версии 4.0, или Personal Web Service для операционной системы Windows 95.

    Документация и литература

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

    Среди удачных назовем книгу Стефана Дэвиса с названием "Learn Java Now", которая может служить учебником по языку Java для тех, кто никогда не программировал на С и С++.

    Среди переводных книг, которые можно встретить в продаже, отметим книгу Джона Родли "Создание JAVA-апплетов". Эта книга рассчитана на серьезных программистов, хорошо знающих язык программирования Java. Однако для тех, кто только начинает изучать язык Java, она может оказаться слишком сложной.

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

    Через сеть Internet вам доступны не только бесплатные средства разработки приложений и аплетов Java, но и грандиозные запасы документации и примеров программ.

    В качестве отправной точки для поиска вы можете выбрать сервер разработчика этого языка - фирмы Sun Microsystems, расположенный по адресу http://www.sun.com. Помимо документации и примеров программ на Java, здесь вы найдете ссылки на другие ресурсы, посвященные этому языку программирования. Попробуйте также воспользоваться поисковыми серверами, такими как Jahoo! и Alta Vista, указав в качестве ключевого слово "Java".

    Обращаем ваше внимание на cловарь Java-терминов, подготовленный Sun Microsystems специально для российских разработчиков приложений Java.

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

Sun Microsystems Inc. Corporate Information Our partners News and Events Search on site Java Computing Products and Solutions Technologies and Researches Education and Service Content


Мобильность Java

Введение
Инструментарий
Мобильность
 Типы данных
Классы Java
Указатели
Массивы
Сборка мусора
Реализация классов

    Мобильность Java

    Назад Вперед

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

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

    Вспомните хотя бы проблемы, связанные с переносом 16-разрядных приложений Windows в 32-разрядную среду Windows 95 и Windows NT. Даже если вы тщательно следовали всем рекомендациям, разрабатывая приложения так, чтобы они могли работать в будущих версиях Windows, едва ли вам удастся просто перекомпилировать исходные тексты, не изменив в них ни строчки. Ситуация еще больше ухудшается, если вам нужно, например, перенести исходные тексты приложения Windows в среду операционной системы OS/2 или в оболочку X-Windows операционной системы UNIX. А ведь есть еще другие компьютеры и рабочие станции!

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

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

    На рис. 5 мы показали, как приложение, изначально разработанное для Windows NT, переносится на платформу Apple Macintosh.

    pic05.gif (3440 bytes)

    Рис. 5. Перенос приложения с платформы Windows NT на платформу Macintosh

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

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

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

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

    Сказанное иллюстрируется на рис. 6.

    pic06.gif (3169 bytes)

    Рис. 6. Подготовка приложения Java для работы на разных платформах

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

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

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

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

    Еще одна проблема, возникающая при переносе программ, составленных на языке программирования С, заключается в том, что размер области памяти, занимаемой переменными стандартных типов, различный на разных платформах. Например, в среде операционной системы Windows версии 3.1 переменная типа int в программе, составленной на С, занимает 16 бит. В среде Windows NT этот размер составляет 32 бита.

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

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


Базовые типы данных

Введение
Инструментарий
Мобильность
Типы данных
 Классы Java
Указатели
Массивы
Сборка мусора
Реализация классов

    Базовые типы данных

    Назад Вперед

    В языке 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

    Фактически размеры памяти, отведенные для хранения переменной, могут отличаться от приведенных выше, например, для хранения переменной типа 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

Введение
Инструментарий
Мобильность
Типы данных
Классы Java
 Указатели
Массивы
Сборка мусора
Реализация классов

    Библиотеки классов Java

    Назад Вперед

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

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

    Встроенные классы

    В языке Java все классы происходят от класса Object, и, соответственно, наследуют методы этого класса. Некоторые библиотеки классов подключаются автоматически, и мы будем называть их встроенными. К таким относится, в частности, библиотека с названием java.lang. Другие библиотеки классов вы должны подключать в исходном тексте приложения Java явным образом с помощью оператора import.

    Замещающие классы

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

    Базовый тип данных | Замещающий класс
boolean | Boolean
char | Character
int | Integer
long | Long
float | Float
double | Double

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

    Класс String

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

    Хотя в языке Java не допускается перезагрузка (переопределение) операторов, для объектов класса Stirng и объектов всех произошедших от него классов сделана встроенная перезагрузка операторов "+" и "+=". С помощью этих операторов можно выполнять слияние текстовых строк, например:

    System.out.println("x = " + x + '\n');

    Здесь в качестве параметра функции println передается текстовая строка, составленная из трех компонент: строки "x = ", числа x и символа перехода на следующую строку '\n'. Значение переменной x автоматически преобразуется в текстовую строку (что выполняется только для текстовых строк) и полученная таким образом текстовая строка сливается со строкой "x = ".

    Другие встроенные классы

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

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

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

    Подключаемые библиотеки классов

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

    Библиотека классов java.util

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

    Библиотека классов java.io

    В библиотеке классов java.io собраны классы, имеющие отношение к вводу и выводу данных через потоки. Заметим, что с использованием этих классов можно работать не только с потоками байт, но также и с потоками данных других типов, например числами int или текстовыми строками.

    Библиотека классов java.net

    Язык программирования Java разрабатывался в предположении, что им будут пользоваться для создания сетевых приложений. Поэтому было бы странно, если бы в составе среды разработки приложений Java не поставлялась библиотека классов для работы в сети. Библиотека классов java.net предназначена как раз для этого. Она содержит классы, с помощью которых можно работать с универсальными сетевыми адресами URL, передавать данные с использованием сокетов TCP и UDP, выполнять различные операции с адресами IP. Эта библиотека содержит также классы для выполнения преобразований двоичных данных в текстовый формат, что часто бывает необходимо.

    В качестве примера приложения, составленного на языке программирования Java и ориентированного на работу в сети Internet, можно привести игру Java Color Lines (рис. 7).

    pic07sm.gif (3435 bytes) Рис. 7. Сетевая игра Java Color Lines.

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

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

    Библиотека классов java.awt

    Для создания пользовательского интерфейса аплеты Java могут и должны использовать библиотеку классов java.awt. AWT - это сокращение от Abstract Window Toolkit (инструментарий для работы с абстрактными окнами).

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

    Библиотека классов java.awt.image

    В среде любой операционной системы работа с графическими изображениями является достаточно сложной задачей. В операционной системе Windows для этого применяется графический интерфейс GDI. Если вы будете рисовать графические изображения в среде OS/2 или X-Windows, вам, очевидно, придется использовать другой программный интерфейс. Большую сложность также вызывает разбор заголовков графических файлов, так как они могут иметь различный формат и иногда содержат неправильную или противоречивую информацию.

    Когда вы программируете на Java, рисование и обработка графических изображений выполняется намного проще, так как вам доступна специально предназначенная для этого библиотека классов java.awt.image. Помимо широкого разнообразия и удобства определенных в ней классов и методов, отметим способность этой библиотеки работать с графическими изображениями в формате GIF. Этот формат широко используется в Internet, так как он позволяет сжимать файлы графических изображений во много раз без потери качества за счет устранения избыточности.

    Библиотека классов java.awt.peer

    Библиотека классов java.awt.peer служит для подключения компонент AWT (например, кнопок, списков, полей редактирования текстовой информации, переключателей и так далее) к реализациям, зависящим от платформы, в процессе создания этих компонент.

    Библиотека классов java.applet

    Как нетрудно догадаться из названия, библиотека классов java.applet инкапсулирует поведение аплетов Java. Когда вы будете создавать свои аплеты, вам будет нужен класс Applet, расположенный в этой библиотеке классов. Дополнительно в библиотеке классов java.applet определены интерфейсы для подключения аплетов к содержащим их документам и классы для проигрывания звуковых фрагментов.


Указатели, которых нет

Введение
Инструментарий
Мобильность
Типы данных
Классы Java
Указатели
 Массивы
Сборка мусора
Реализация классов

    Указатели, которых нет

    Назад Вперед

    Самая большая и шокирующая новость для тех, кто раньше программировал на С, а теперь занялся изучением 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 реализован механизм массивов, исключающих необходимость использования указателей.


Массивы в Java

Введение
Инструментарий
Мобильность
Типы данных
Классы Java
Указатели
Массивы
 Сборка мусора
Реализация классов

    Массивы в 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 являются объектами некоторого встроенного класса. Для этого класса существует возможность определить размер массива, обратившись к элементу данных класса с именем length, например:

    int[] nAnotherNumbers;
nAnotherNumbers = new int[15];
for(int i = 0; i < nAnotherNumbers.length; i++)
{
  nAnotherNumbers[i] = nInitialValue;
}

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


Сборка мусора

Введение
Инструментарий
Мобильность
Типы данных
Классы Java
Указатели
Массивы
Сборка мусора Реализация классов

    Сборка мусора

    Назад Вперед

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

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

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


Особенности реализации классов в Java

Введение
Инструментарий
Мобильность
Типы данных
Классы Java
Указатели
Массивы
Сборка мусора
Реализация классов

    Особенности реализации классов в Java

    Назад Вперед

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

    Определение класса

    Для создания классов вы можете использовать только ключевое слово class. Что же касается union, то это ключевое слово в Java не применяется.

    В языке программирования С++ описание класса может быть отделено от его определения. Для Java это не так - описание класса недопустимо. Все методы должны быть определены внутри определения класса.

    Недопустимо определение вложенных классов.

    В Java также нет шаблонов. Вы можете создавать классы только на базе других классов.

    Объект класса создается при помощи ключевого слова new, однако вы не можете удалить объект явным образом, так как ключевое слово delete языка программирования С++ в Java не используется.

    При определении класса вы не можете указать деструктор. Функции удаления объектов Java из памяти выполняет система сборки мусора.

    Внутри одного исходного файла вы можете определить только один общедоступный класс public.

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

    Определение методов

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

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

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

    Методы не могут быть определены как inline.

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

    Переопределение операторов

    В языке С++ вы могли переопределить операторы, такие как +, -, ++ и так далее. Язык Java не допускает переопределение, что сделано для упрощения программирования. Тем не менее, операторы "+" и "+=" перегружены по умолчанию для выполнения операции слияния текстовых строк класса String.

    Интерфейсы

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

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

    Ссылки на методы класса

    Так как в Java нет указателей, нет возможности ссылаться на методы с помощью оператора ->. Для ссылки на метод класса используется только оператор "точка".

    Оператор "::" также не определен в Java. Если вам необходимо вызвать метод из базового класса, следует использовать ключевое слово super.

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

    С помощью ключевого слова extends вы можете унаследовать один класс (дочерний) от другого (базового).

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

    Для ссылки на методы базового класса вы должны использовать ключевое слово super.

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


Рис. 1. Главное окно приложения

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

pic01.gif (15212 bytes)

Рис. 1. Главное окно приложения


Рис. 2. Визуальные средства разработки Java WorkShop

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

pic02.gif (14579 bytes)

Рис. 2. Визуальные средства разработки Java WorkShop 2.0


Рис. 3. Проектирование логики работы приложения

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

pic03.gif (16895 bytes)

Рис. 3. Проектирование логики работы приложения


Рис. 4. Визуальное проектирование пользовательского интерфейса

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

pic04.gif (6397 bytes)

Рис. 4. Визуальное проектирование пользовательского интерфейса


Рис. 7. Сетевая игра Java Color Lines

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

pic07.gif (10929 bytes)

Рис. 7. Сетевая игра Java Color Lines


Многопоточность
Процессы, потоки и приоритеты
Многопоточность в Java
Многопоточность и анимация
Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Многопоточность

    Назад Вперед

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

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

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

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


Процессы, потоки и приоритеты
Многопоточность в Java
Многопоточность и анимация
Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Процессы, потоки и приоритеты

    Назад Вперед

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

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

    Процесс

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

    Поток

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

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

    Приоритеты потоков в приложениях Java

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

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

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

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

    Каким именно образом?

    Приложения Java могут указывать три значения для приоритетов потоков. Это NORM_PRIORITY, MAX_PRIORITY и MIN_PRIORITY.

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

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


Реализация многопоточности в Java

Многопоточность
Процессы, потоки и приоритеты
Многопоточность в Java
Многопоточность и анимация
Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Реализация многопоточности в Java

    Назад Вперед
    должны воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания потоков, управления их состоянием и синхронизации.

    Как пользоваться классом Thread?

    Есть две возможности.

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

    Второй способ особенно удобен в тех случаях, когда ваш класс должен быть унаследован от какого-либо другого класса (например, от класса Applet) и при этом вам нужна многопоточность. Так как в языке программирования Java нет множественного наследования, невозможно создать класс, для которого в качестве родительского будут выступать классы Applet и Thread. В этом случае реализация интерфейса Runnable является единственным способом решения задачи.

    Методы класса Thread

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

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

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

    Поля

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

    • NORM_PRIORITY

    Нормальный

    public final static int NORM_PRIORITY;
    • MAX_PRIORITY

    Максимальный

    public final static int MAX_PRIORITY;
    • MIN_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);

    Методы

    • activeCount

    Текущее количество активных потоков в группе, к которой принадлежит поток

    public static int activeCount();
    • checkAccess

    Текущему потоку разрешается изменять объект Thread

    public void checkAccesss();
    • countStackFrames

    Определение количества фреймов в стеке

    public int countStackFrames();
    • currentThread

    Определение текущего работающего потока

    public static Thread currentThread();
    • destroy

    Принудительное завершение работы потока

    public void destroy();
    • dumpStack

    Вывод текущего содержимого стека для отладки

    public static void dumpStack();
    • enumerate

    Получение всех объектов Tread данной группы

    public static int enumerate(Thread  tarray[]);
    • getName

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

    public final String getName();
    • getPriority

    Определение текущего приоритета потока

    public final int getPriority();
    • getThreadGroup

    Определение группы, к которой принадлежит поток

    public final ThreadGroup getThreadGroup();
    • interrupt

    Прерывание потока

    public void interrupt();
    • interrupted

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

    public static boolean interrupted();
    • isAlive

    Определение, выполняется поток или нет

    public final boolean isAlive();
    • isDaemon

    Определение, является ли поток демоном

    public final boolean isDaemon();
    • isInterrupted

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

    public boolean isInterrupted();
    • join

    Ожидание завершения потока

    public final void join();

    Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах

    public final void join(long millis);

    Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах и наносекундах

    public final void join(long  millis, int  nanos);
    • resume

    Запуск временно приостановленного потока

    public final void resume();
    • run

    Метод вызывается в том случае, если поток был создан как объект с интерфейсом Runnable

    public void run();
    • setDaemon

    Установка для потока режима демона

    public final void setDaemon(boolean on);
    • setName

    Устаовка имени потока

    public final void setName(String name);
    • setPriority

    Установка приоритета потока

    public final void setPriority(int newPriority);
    • sleep

    Задержка потока на заднное время. Время задается в миллисекундах и наносекундах

    public static void sleep(long millis);

    Задержка потока на заднное время. Время задается в миллисекундах и наносекундах

    public static void sleep(long millis, int nanos);
    • start

    Запуск потока на выполнение

    public void start();
    • stop

    Остановка выполнения потока

    public final void stop();

    Аварийная остановка выполнения потока с заданным исключением

    public final void stop(Throwable obj);
    • suspend

    Приостановка потока

    public final void suspend();
    • toString

    Строка, представляющая объект-поток

    public String toString();
    • yield

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

    public static void yield();

    Создание дочернего класса на базе класса 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, содержащую аплет.

    Реализация интерфейса 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
Многопоточность и анимация
Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Применение многопоточности для анимации

    Назад Вперед

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

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

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

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

    Можно ли воспользоваться методом paint для периодической перерисовки окна аплета, организовав в нем, например, бесконечный цикл с задержкой?

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

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


Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Синхронизация потоков

    Назад Вперед

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

    Для чего и когда она нужна?

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

    Поясним это на простом примере.

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

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

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

    Допустим, события разворачиваются следующим образом:

    • первый процесс проверяет состояние текущего счета и убеждается, что на нем хранится 5 млн. долларов;
    • второй процесс проверяет состояние текущего счета и также убеждается, что на нем хранится 5 млн. долларов;
    • первый процесс уменьшает счет на 3 млн. долларов и записывает остаток (2 млн. долларов) на текущий счет;
    • второй процесс выполняет ту же самую операцию, так как после проверки считает, что на счету по-прежнему хранится 5 млн. долларов.

    В результате получилось, что со счета, на котором находилось 5 млн. долларов, было снято 6 млн. долларов, и при этом там осталось еще 2 млн. долларов! Итого - банку нанесен ущерб в 3 млн. долларов.

    Как же составить программу уменьшения счета, чтобы она не позволяла вытворять подобное?

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

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

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

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

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

    Синхронизация методов

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

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

    public synchronized void decrement()
{
  . . .
}

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

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

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

    . . .
synchronized(Account)
{
  if(Account.check(3000000))
     Account.decrement(3000000);
}
. . .

    Здесь синхронизация выполняется для объекта Account.

    Блокировка потока

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

    Блокировка на заданный период времени

    С помощью метода sleep можно заблокировать поток на заданный период времени:

    try
{
  Thread.sleep(500);
}
catch (InterruptedException ee)
{
  . . .
}

    В данном примере работа потока Thread приостанавливается на 500 миллисекунд. Заметим, что во время ожидания приостановленный поток не отнимает ресурсы процессора.

    Так как метод sleep может создавать исключение InterruptedException, необходимо предусмотреть его обработку. Для этого мы использовали операторы try и catch.

    Временная приостановка и возобновление работы

    Методы suspend и resume позволяют, соответственно, временно приостанавливать и возобновлять работу потока.

    В следующем фрагменте кода поток m_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;
}

    Ожидание извещения

    Если вам нужно организовать взаимодействие потоков таким образом, чтобы один поток управлял работой другого или других потоков, вы можете воспользоваться методами wait, notify и notifyAll, определенными в классе Object.

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

    Как пользоваться методами wait, notify и notifyAll?

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

    public synchronized void run()
{
  while (true)
  {
    . . .
    try
    {
      this.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.

    Ожидание завершения потока

    С помощью метода join вы можете выполнять ожидание завершения работы потока, для которой этот метод вызван.

    Существует три определения метода join:

    public final void join();
public final void join(long millis);
public final void join(long millis,
   int nanos);

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


Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Потоки-демоны

    Назад Вперед

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

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

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


Аплет Rectangles
Исходные тексты
Описание текстов

    Аплет Rectangles

    Назад Вперед

    В качестве примера многопоточного приложения мы приведем аплет Rectangles (рис. 1). Он создает три потока. Первый поток рисует в окне аплета прямоугольники случайного размера и цвета, второй - эллипсы, а третий управляет потоком рисования эллипсов.

    pic01.gif (7162 bytes)

    Рис. 1. Окно аплета Rectangles

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


Исходные тексты аплета Rectangles

Многопоточность
Процессы, потоки и приоритеты
Многопоточность в Java
Многопоточность и анимация
Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Исходные тексты аплета Rectangles

    Назад Вперед

    Исходные тексты аплета Rectangles приведены в листинге 1.

    Листинг 1. Файл Rectangles,java

    import java.applet.*;
import java.awt.*;
    public class Rectangles extends Applet
{
  DrawRectangles m_DrawRectThread = null;
  DrawEllipse m_DrawEllipseThread = null;
  NotifyTask m_NotifyTaskThread = null
      public String getAppletInfo()
  {
    return "Name: Rectangles";
  }
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension = getSize();
        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);
  }
      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();
    }
        if (m_NotifyTaskThread == null)
    {
      m_NotifyTaskThread =
        new NotifyTask(m_DrawEllipseThread);
      m_NotifyTaskThread.start();
    }
  }

  public void stop()
  {
    if (m_DrawRectThread != null)
    {
      m_DrawRectThread.stop();
      m_DrawRectThread = null;
    }
        if (m_DrawEllipseThread == null)
    {
      m_DrawEllipseThread.stop();
      m_DrawEllipseThread = null;
    }
        if (m_NotifyTaskThread != null)
    {
      m_NotifyTaskThread.stop();
      m_NotifyTaskThread = null;
    }
  }
}
    class DrawRectangles extends Thread
{
  Graphics g;
  Dimension dimAppWndDimension;
      public DrawRectangles(Applet Appl)
  {
    g = Appl.getGraphics();
    dimAppWndDimension = Appl.getSize();
  }
      public void run()
  {
    while (true)
    {
      int x, y, width, height;
      int rColor, gColor, bColor;
          x =      (int)(dimAppWndDimension.width
            * Math.random());
      y =      (int)(dimAppWndDimension.height
            * Math.random());
      width  = (int)(dimAppWndDimension.width
            * Math.random()) / 2;
      height = (int)(dimAppWndDimension.height
            * Math.random()) / 2;
          rColor = (int)(255 * Math.random());
      gColor = (int)(255 * Math.random());
      bColor = (int)(255 * Math.random());
          g.setColor(new Color(rColor,
        gColor, bColor));
      g.fillRect(x, y, width, height);
          try
      {
    Thread.sleep(50);
      }
      catch (InterruptedException e)
      {
    stop();
      }
    }
  }
}
    class DrawEllipse extends Thread
{
  Graphics g;
  Dimension dimAppWndDimension;
      public DrawEllipse(Applet Appl)
  {
    g = Appl.getGraphics();
    dimAppWndDimension = Appl.getSize();
  }
      public synchronized void run()
  {
    while (true)
    {
      int x, y, width, height;
      int rColor, gColor, bColor;
          x =      (int)(dimAppWndDimension.width
          * Math.random());
      y =      (int)(dimAppWndDimension.height
          * Math.random());
      width  = (int)(dimAppWndDimension.width
          * Math.random()) / 2;
      height = (int)(dimAppWndDimension.height
          * Math.random()) / 2;
          rColor = (int)(255 * Math.random());
      gColor = (int)(255 * Math.random());
      bColor = (int)(255 * Math.random());
          g.setColor(new Color(rColor,
         gColor, bColor));
      g.fillOval(x, y, width, height);
          try
      {
    this.wait();
      }
      catch (InterruptedException e)
      {
      }
    }
  }
}
    class NotifyTask extends Thread
{
  Thread STask;
      public NotifyTask(Thread SynchroTask)
  {
    STask = SynchroTask;
  }
      public void run()
  {
    while (true)
    {
      try
      {
    Thread.sleep(30);
      }
      catch (InterruptedException e)
      {
      }
          synchronized(STask)
      {
        STask.notify();
      }
    }
  }
}


Описание исходных текстов аплета Rectangles

Многопоточность
Процессы, потоки и приоритеты
Многопоточность в Java
Многопоточность и анимация
Синхронизация потоков
Потоки-демоны
Аплет Rectangles
Исходные тексты
Описание текстов

    Описание исходных текстов аплета Rectangles

    Назад Вперед

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

    Что же касается основного класса аплета, то он унаследован, как обычно, от класса Applet и не реализует интерфейс Runnable:

    public class Rectangles extends Applet
{
  . . .
}

    Поля класса Rectangles

    В классе Rectangles мы определили три поля с именами m_DrawRectThread, m_DrawEllipseThread и m_NotifyTaskThread:

    DrawRectangles m_DrawRectThread = null;
DrawEllipse m_DrawEllipseThread = null;
NotifyTask m_NotifyTaskThread = null

    Эти поля являются ссылками на классы, соответственно DrawRectangles, DrawEllipse и NotifyTask . Первый из них создан для рисования прямоугольников, второй - эллипсов, а третий - для управления потоком рисования эллипсов.

    Указанные поля инициализируются занчением null, что соответствует неработающим или несозданным задачам.

    Метод start класса Rectangles

    Этот метод последовательно создает три потока и запускает их на выполнение:

    if(m_DrawRectThread == null)
{
  m_DrawRectThread =
    new DrawRectangles(this);
  m_DrawRectThread.start();
}
    if(m_DrawEllipseThread == null)
{
  m_DrawEllipseThread =
    new DrawEllipse(this);
  m_DrawEllipseThread.start();
}
    if(m_NotifyTaskThread == null)
{
  m_NotifyTaskThread =
    new NotifyTask(m_DrawEllipseThread);
  m_NotifyTaskThread.start();
}

    В качестве параметра конструкторам классов DrawRectangles и DrawEllipse мы передаем ссылку на аплет Rectangles. Эта ссылка будет нужна для получения контекста отображения и рисования геометрических фигур.

    Поток класса NotifyTask будет управлять работой потока DrawEllipse, поэтому мы передаем его конструктору ссылку на соответствующий объект m_DrawEllipseThread.

    Метод stop класса Rectangles

    Когда пользователь покидает страницу сервера Web с аплетом, метод stop класса Rectangles последовательно останавливает gjnjrb рисования прямоугольников и эллипсов, а также управляющий поток:

    if(m_DrawRectThread != null)
{
  m_DrawRectThread.stop();
  m_DrawRectThread = null;
}
    if(m_DrawEllipseThread == null)
{
  m_DrawEllipseThread.stop();
  m_DrawEllipseThread = null;
}
    if(m_NotifyTaskThread != null)
{
  m_NotifyTaskThread.stop();
  m_NotifyTaskThread = null;
}

    Поля класса DrawRectangles

    Класс DrawRectangles определен для потока рисования прямоугольников:

    class DrawRectangles extends Thread
{
  . . .
}

    В поле g класа хранится контекст отображения окна аплета, а в поле dimAppWndDimension - размеры этого окна:

    Graphics g;
Dimension dimAppWndDimension;

    Значения этих полей определяются конструктором класса по ссылке на главный класс аплета.

    Конструктор класса DrawRectangles

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

    public DrawRectangles(Applet Appl)
{
  g = Appl.getGraphics();
  dimAppWndDimension = Appl.getSize();
}

    Метод run класса DrawRectangles

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

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

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

    В качестве генератора случайных чисел мы используем метод 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 класса DrawEllipse

    Класс DrawEllipse очень похож на только что рассмотренный класс DrawRectangles. Отличие есть только в финальном фрагменте метода run, который мы и рассмотрим.

    Вместо задержки на 50 миллисекунд метод run из класса DrawEllipse переходит в состояние ожидания извещения, вызывая метод wait:

    try
{
  this.wait();
}
catch (InterruptedException e)
{
}

    Это извещение создается управляющим потоком класса NotifyTask, к описанию которого мы переходим.

    Поля класса NotifyTask

    В классе NotifyTask мы определили одно поле STask класса Thread. Это поле которое хранит ссылку на поток, работой которого управляет данный класс:

    class NotifyTask extends Thread
{
  Thread STask;
   . . .
}

    Конструктор класса NotifyTask

    Конструктор класса NotifyTask записывает в поле STask ссылку на задачу рисования эллипсов:

    public NotifyTask(Thread SynchroTask)
{
  STask = SynchroTask;
}

    Метод run класса NotifyTask

    Метод run класса NotifyTask периодически разблокирует поток рисования эллипсов, вызывая для этого метод notify в цилке с задержкой 30 миллисекунд. Обращение к объекту STask, который хранит ссылку на поток рисования эллипсов, выполняется с использованием синхронизации:

    public void run()
{
  while (true)
  {
    try
    {
      Thread.sleep(30);
    }
    catch (InterruptedException e)
    {
    }
        synchronized(STask)
    {
      STask.notify();
    }
  }
}


Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Работа с файлами

    Назад Вперед

    Библиотека классов языка программирования Java содержит многочисленные средства, предназначенные для работы с файлами. И хотя аплеты не имеют доступа к локальным файлам, расположенным на компьютере пользователя, они могут обращаться к файлам, которые находятся в каталоге сервера Web. Автономные приложения Java могут работать как с локальными, так и с удаленными файлами (через сеть Internet или Intranet).

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


Классы Java для работы с потоками

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Классы Java для работы с потоками

    Назад Вперед

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

    • стандартные потоки ввода и вывода;
    • потоки, связанные с локальными файлами;
    • потоки, связанные с файлами в оперативной памяти;
    • потоки, связанные с удаленными файлами

    Рассмотрим кратко классы, связанные с потоками.

    Стандартные потоки

    Для работы со стандартными потоками в классе System имеется три статических объекта: System.in, System.out и System.err. По своему назначению эти потоки больше всего напоминают стандартные потоки ввода, вывода и вывода сообщений об ошибках операционной системы MS-DOS.

    Поток System.in связан с клавиатурой, поток System.out и System.err - с консолью приложения Java.

    Базовые классы для работы с файлами и потоками

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

    Все основные классы, интересующие нас в этой главе, произошли от класса Object (рис. 1).

    pic01.gif (2917 bytes)

    Рис. 1. Основные классы для работы с файлами и потоками

    Класс InputStream

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

    Класс OutputStream

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

    Класс RandomAccesFile

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

    Класс File

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

    Класс FileDescriptor

    C помощью класса FileDescriptor вы можете проверить идентификатор открытого файла.

    Класс StreamTokenizer

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

    Производные от класса InputStream

    От класса InputStream производится много других классов, как это показано на рис. 2.

    pic02.gif (5262 bytes)

    Рис. 2. Классы, производные от класса InputStream

    Класс FilterInputStream

    Класс FilterInputStream, который происходит непосредственно от класса InputStream, является абстрактным классом, на базе которого созданы классы BufferedInputStream, DataInputStream, LineNumberInputStream и PushBackInputStream. Непосредственно класс FilterInputStream не используется в приложениях Java, так как, во-первых, он является абстрактным и предназначен для переопределения методов базового класса InputStream, а во-вторых, наиболее полезные методы для работы с потоками ввода имеются в классах, созданных на базе класса FilterInputStream.

    Класс BufferedInputStream

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

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

    Класс DataInputStream

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

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

    Так же как и конструктор класса BufferedInputStream, конструктор класса DataInputStream должен получить через свои параметр ссылку на объект класса InputStream.

    Класс LineNumberInputStream

    С помощью класса LineNumberInputStream вы можете работать с текстовыми потоками, состоящими из отдельных строк, разделенных символами возврата каретки \r и перехода на следующую строку \n. Методы этого класса позволяют следить за нумерацией строк в таких потоках.

    Класс PushBackInputStream

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

    Класс ByteArrayInputStream

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

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

    Класс StringBufferInputStream

    Класс StringBufferInputStream позволяет создавать потоки ввода на базе строк класса String, используя при этом только младшие байты хранящихся в такой строке символов. Этот класс может служить дополнением для класса ByteArrayInputStream, который также предназначен для создания потоков на базе данных из оперативной памяти.

    Класс FileInputStream

    Этот класс позволяет создать поток ввода на базе класса File или FileDescriptor.

    Класс PipedInputStream

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

    Класс SequenceInputStream

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

    Производные от класса OutputStream

    Класс OutputStream предназначен для создания потоков вывода. Приложения, как правило, непосредственно не используют этот класс для операций вывода, так же как и класс InputStream для операций ввода. Вместо этого применяются классы, иерархия которых показана на рис. 3.

    pic03.gif (3919 bytes)

    Рис. 3. Классы, производные от класса OutputtStream

    Рассмотрим кратко назначение этих классов.

    Класс FilterOutputStream

    Абстрактный класс FilterOutputStream служит прослойкой между классом OutputStream и классами BufferedOutputStream, DataOutputStream, а также PrintStream. Он выполняет роль, аналогичную роли рассмотренного ранее класса FilterIntputStream.

    Класс BufferedOutputStream

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

    Класс DataOutputStream

    С помощью класса DataOutputStream приложения Java могут выполнять форматированный вывод данных. Для ввода форматированных данных вы должны создать входной поток с использованием класса DataInputStream, о котором мы уже говорили. Класс DataOutputStream реализует интерфейс DataOutput.

    Класс PrintStream

    Потоки, созданные с использованием класса PrintStream, предназначены для форматного вывода данных различных типов с целью их визуального представления в виде текстовой строки. Аналогичная операция в языке программирования С выполнялась функцией printf.

    Класс ByteArrayOutputStream

    С помощью класса ByteArrayOutputStream можно создать поток вывода в оперативной памяти.

    Класс FileOutputStream

    Этот класс позволяет создать поток вывода на базе класса File или FileDescriptor.

    Класс PipedOutputStream

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


Работа со стандартными потоками

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Работа со стандартными потоками

    Назад Вперед

    Приложению Java доступны три стандратных потока, которые всегда открыты: стандартный поток ввода, стандартный поток вывода и стандартный поток вывода сообщений об ошибках.

    Все перечисленные выше потоки определены в классе System как статические поля с именами, соответственно, in, out и err:

    public static PrintStream err;
public static InputStream in;
public static PrintStream out;

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

    Стандартный поток ввода

    Стандартный поток ввода 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.


Создание потоков, связанных с файлами

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
 Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Создание потоков, связанных с файлами

    Назад Вперед

    Если вам нужно создать входной или выходной поток, связанный с локальным файлом, следует воспользоваться классами из библиотеки Java, созданными на базе классов InputStream и OutputStream. Мы уже кратко рассказывали об этих классах в разделе "Классы Java для работы с потоками". Однако методика использования перечисленных в этом разделе классов может показаться довольно странной.

    В чем эта странность?

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

    Поясним сказанное на примере.

    Пусть, например, нам нужен выходной поток для записи форматированных данных (скажем, текстовых строк класса String). Казалось бы, достаточно создать объект класса DataOutputStream, - и дело сделано. Однако не все так просто.

    В классе DataOutputStream предусмотрен только один конструктор, которому в качестве параметра необходимо передать ссылку на объект класса OutputStream:

    public DataOutputStream(OutputStream out);

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

    public OutputStream();

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

    Что же делать?

    Создание потока для форматированного обмена данными

    Оказывается, создание потоков, связанных с файлами и предназначенных для форматированного ввода или вывода, необходимо выполнять в несколько приемов. При этом вначале необходимо создать потоки на базе класса FileOutputStream или FileInputStream, а затем передать ссылку на созданный поток констркутору класса DataOutputStream или DataInputStream.

    В классах FileOutputStream и FileInputStream предусмотрены конструкторы, которым в качестве параметра передается либо ссылка на объект класса File, либо ссылка на объект класса FileDescriptor, либо, наконец, текстовая строка пути к файлу:

    public FileOutputStream(File file);
public FileOutputStream(
  FileDescriptor fdObj);
public FileOutputStream(String name);

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

    Добавление буферизации

    А что, если нам нужен не простой выходной поток, а буферизованный?

    Здесь нам может помочь класс 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")));

    Исключения при создании потоков

    При создании потоков на базе классов FileOutputStream и FileInputStream могут возникать исключения FileNotFoundException, SecurityException, IOException.

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

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

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


Запись данных в поток и чтение данных из потока

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Запись данных в поток и чтение данных из потока

    Назад Вперед

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

    Простейшие методы

    Создав выходной поток на базе класса 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.

    Методы для чтения и записи форматированных данных

    Вместо того чтобы записывать в потоки и читать оттуда отдельные байты или массивы байт, программисты обычно предпочитают пользоваться намного более удобными методами классов 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. Первое из них возникает в случае ошибки, а второе - при достижении конца входного потока в процессе чтения.


Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Закрывание потоков

    Назад Вперед

    Работая с файлами в среде MS-DOS или Windows средствами языка программирования С вы должны были закрывать ненужные более файлы. Так как в системе интерпертации приложений Java есть процесс сборки мусора, возникает вопрос - выполняет ли он автоматическое закрывание потоков, с которыми приложение завершило работу?

    Оказывается, процесс сборки мусора не делает ничего подобного!

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


Принудительный сброс буферов

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Принудительный сброс буферов

    Назад Вперед

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

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

Sun Microsystems Inc. Corporate Information Our partners


Потоки в оперативной памяти

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Потоки в оперативной памяти

    Назад Вперед

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

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

    Ранее мы отмечали, что в библиотеке классов Java есть три класса, специально предназначенных для создания потоков в оперативной памяти. Это классы ByteArrayOutputStream, ByteArrayInputStream и StringBufferInputStream.

    Класс 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.

    Класс ByteArrayInputStream

    С помощью класса 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, что позволяет организовать форматный ввод данных.

    Класс StringBufferInputStream

    Класс StringBufferInputStream предназначен для создания входного потока на базе текстовой строки класса String. Ссылка на эту строку передается конструктору класса StringBufferInputStream через параметр:

    public StringBufferInputStream(String s);

    В классе StringBufferInputStream определены те же методы, что и в только что рассмотренном классе ByteArrayInputStream. Для более удобной работы вы, вероятно, создадите на базе потока класса StringBufferInputStream поток класса DataInputStream.


Класс StreamTokenizer для разбора входных потоков

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Класс StreamTokenizer для разбора входных потоков

    Назад Вперед

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

    Конструктор класса StreamTokenizer

    Для создание объектов класса StreamTokenizer предусмотрен всего один конструктор:

    public StreamTokenizer(InputStream istream);

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

    Методы класса StreamTokenizer

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

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

    Ниже мы привели прототипы методов, предназначенных для настройки параметров разборщика:

    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_WORDTT_WORD | Из потока было извлечено слово
TT_NUMBERTT_NUMBER | Из потока было извлечено численное значение
TT_EOLTT_EOL | Обнаружен конец строки. Это значение возвращается только в том случае, если при настройке параметров разборщика был вызван метод eolIsSignficant
TT_EOFTT_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();


Класс StringTokenizer

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Класс StringTokenizer

    Назад Вперед

    Рассказывая о классе StreamTokenizer, нельзя не упомянуть о другом классе с похожим названием и назначением, а именно о классе StringTokenizer.

    Определение этого класса достаточно компактно.

    • Конструкторы
    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 countTokenss();
  public boolean hasMoreElements();
  public boolean hasMoreTokenss();
  public Object nextElement();
}

    Класс StringTokenizer не имеет никакого отношения к потокам, так как предназначен для выделения отдельных элементов из строк типа String.

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

    Рассмотрим кратко методы класса StringTokenizer.

    Для разбора строки приложение должно организовать цикл, вызывая в нем метод nextToken. Условием завершения цикла может быть либо возникновение исключения NoSuchElementException, либо возврат значения false методами hasMoreElements или hasMoreTokens.

    Метод countTokens позволяет определить, сколько раз был вызван метод nextToken перед возникновением исключения NoSuchElementException.


Работа с файлами и каталогами при помощи класса File

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Работа с файлами и каталогами при помощи класса File

    Назад Вперед

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

    Создание объекта класса 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");

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

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

    Проверка существования файла или каталога

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

    public boolean exists();

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

    Проверка возможности чтения и записи

    Методы canRead и canWrite позволяют проверить возможность чтения из файла и записи в файл, соответственно:

    public boolean canRead();
public boolean canWrite();

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

    Определение типа объекта - файл или каталог

    С помощью методов isDirectory и isFile вы можете проверить, чему соответствует созданный объект класса File - каталогу или файлу:

    public boolean isDirectory();
public boolean isFile();

    Получение имени файла или каталога

    Метод getName возвращает имя файла или каталога для заданного объекта класса File (имя выделяется из пути):

    public String getName();

    Получение абсолютного пути к каталогу

    Метод getAbsolutePath возвращает абсолютный путь к файлу или каталогу, который может быть машинно-зависимым:

    public String getAbsolutePath();

    Определение типа указанного пути - абсолютный или относительный

    С помощью метода isAbsolute вы можете определить, соответствует ли данный объект класса File файлу или каталогу, заданному абсолютным (полным) путем, либо относительным путем:

    public boolean isAbsolute();

    Определение пути к файлу или каталогу

    Метод getPath позволяет определить машинно-независимый путь файла или каталога:

    public String getPath();

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

    Если вам нужно определить родительский каталог для объекта класса File, то это можно сделать методом getParent:

    public String getParent();

    Определение длины файла в байтах

    Длину файла в байтах можно определить с помощью метода length:

    public long length();

    Определение времени последней модификации файла или каталога

    Для определения времени последней модификации файла или каталога вы можете вызвать метод lastModified:

    public long lastModified();

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

    Получение текстового представления объекта

    Метод toString возвращает текстовую строку, представляющую объект класса File:

    public String toString();

    Получение значения хэш-кода

    Метод hashCode возвращает значение хэш-кода, соответствующего объекту File:

    public int hashCode();

    Удаление файлов и каталогов

    Для удаления ненужного файла или каталога вы должны создать соответствующий объект File и затем вызвать метод delete:

    public boolean delete();

    Создание каталогов

    С помощью методов mkdir и mkdirs можно создавать новые каталоги:

    public boolean mkdir();
public boolean mkdirs();

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

    Переименование файлов и каталогов

    Для переименования файла или каталога вы должны создать два объекта класса File, один из которых соответствует старому имени, а второй - новому. Затем для перовго из этих объектов нужно вызвать метод renameTo, указав ему в качестве параметра ссылку на второй объект:

    public boolean renameTo(File dest);

    В случае успеха метод возвращает значение true, при возникновении ошибки - false. Может также возникать исключение SecurityException.

    Сравнение объектов класса File

    Для сравнения объектов класса File вы должны использовать метод equals:

    public boolean equals(Object obj);

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

    Получение списка содержимого каталога

    С помощью метода list вы можете получить список содержимого каталога, соответствующего данному объекту класса File. В классе File предусмотрено два варианта этого метода - без параметра и с параметром:

    public String[] list();
public String[] list(FilenameFilter filter);

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


Произвольный доступ к файлам

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Произвольный доступ к файлам

    Назад Вперед

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

    Между тем библиотека классов 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     readUnsignedBytee();
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 writeBytee(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);

    Имена приведенных методов говорят сами за себя, поэтому мы не будем их описывать.


Приложение StreamToken

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Приложение StreamToken

    Назад Вперед

    В приложении StreamToken мы демонстрируем использование класса StreamTokenizer для разбора входного потока.

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

    pic04.gif (5983 bytes)

    Рис. 4. Разбор входного потока в приложении StreamToken

    Обратите внимание, что в процессе разбора значение 3.14 было воспринято как числовое, а 3,14 - нет. Это потому, что при настройке разборщика мы указали, что символ '.' является обычным.

    Исходный текст приложения

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

    Листинг 1. Файл StreamToken.java

    import java.io.*;
    public class StreamToken
{
  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);
          sOut = new String(bKbdInput, 0);
          OutStream = new DataOutputStream(
        new BufferedOutputStream(
          new FileOutputStream(
           "output.txt")));
          OutStream.writeBytes(sOut);
      OutStream.close();
          InStream = new DataInputStream(
        new BufferedInputStream(
          new FileInputStream(
            "output.txt")));
          TokenizerOfStream tos =
        new TokenizerOfStream();
          tos.TokenizeIt(InStream);
      InStream.close();
          System.out.println(
        "Press <Enter> to terminate...");
      System.in.read(bKbdInput);
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
}
    class TokenizerOfStream
{
  public void TokenizeIt(InputStream is)
  {
    StreamTokenizer stok;
    String str;
        try
    {
      stok = new StreamTokenizer(is);
      stok.slashSlashComments(true);
          stok.ordinaryChar('.');
          while(stok.nextToken() !=
         StreamTokenizer.TT_EOF)
      {
        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);
            break;
          }
              case StreamTokenizer.TT_EOL:
          {
            str = new String("> End of line");
            break;
          }
              default:
            {
              if((char)stok.ttype == '"')
              {
                str = new String(
                  "\nTT_WORD >" + stok.sval);
              }
                  else
                str = "> " +
                  String.valueOf(
                    (char)stok.ttype);
            }
        }
            System.out.println(str);
      }
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
}

    Описание исходного текста приложения

    После ввода строки с клавиатуры и записи ее в файл через поток наше приложение создает входной буферизованный поток, как это показано ниже:

    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);
    break;
  }
  case StreamTokenizer.TT_EOL:
  {
    str = new String("> End of line");
    break;
  }
  default:
  {
    if((char)stok.ttype == '"')
      str = new String(
        "\nTT_WORD >" + stok.sval);
    else
      str = "> " + String.valueOf(
       (char)stok.ttype);
  }
}

    На слова и численные значения мы реагируем очень просто - записываем их текстовое представление в рабочую переменную str типа String. При обнаружении конца строки в эту переменную записывается строка End of line.

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

    В заключении метод выводит строку str в стандартный поток вывода, отображая на консоли выделенный элемент потока:

    System.out.println(str);


Приложение DirectFile

Работа с файлами
Классы потоков
Стандартные потоки
Потоки и файлы
Запись и чтение потоков
Закрывание потоков
Сброс буферов
Потоки в памяти
Stream Tokenizer
String Tokenizer
Класс File
Произвольный доступ
Приложение StreamToken
Приложение DirectFile

    Приложение DirectFile

    Назад Вперед

    Для иллюстрации способов работы с классом RandomAccessFile мы подготовили приложение DirectFile, в котором создается небольшая база данных. Эта база данных состоит из двух файлов: файла данных и файла индекса.

    В файле данных хранятся записи, сосотящие из двух полей - текстового и числового. Текстовое поле с названием name хранит строки, закрытые смиволами конца строки "\r\n", а числовое с названием account - значения типа int.

    В меню File нашего приложения есть строки New и View records (рис. 5).

    pic05.gif (2516 bytes)

    Рис. 5. Строки меню File

    С помощью строки New вы можете создать базу данных, состоящую из трех записей. Если выбрать из меню File строку View records, на экране появится диалоговая панель с содержимым этих записей (рис. 6).

    pic06.gif (2365 bytes)

    Рис. 6. Содержимое трех первых полей базы данных

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

    Дамп создаваемого файла данных приведен на рис. 7.

    pic07.gif (1956 bytes)

    Рис. 7. Дамп файла данных

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

    Номер записи | Смещение в файле данных | Поле name | Поле account
0 | 0 | Ivanov | 1000
1 | 12 | Petrov | 2000
2 | 24 | Sidoroff | 3000

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

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

    pic08.gif (1575 bytes)

    Рис. 8. Дамп файла индекса

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

    Исходный текст приложения DirectFile

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

    Листинг 2. Файл DirectFile.java

    import java.awt.*;
import java.io.*;
import java.util.*;
    public class DirectFile
{
  public static void main(String args[])
  {
    MainFrameWnd frame =
      new MainFrameWnd("MenuApp");
    frame.setSize(
    frame.getInsets().left +
      frame.getInsets().right  + 320,
      frame.getInsets().top  +
      frame.getInsets().bottom + 240);
    frame.show();
  }
}
    class MainFrameWnd extends Frame
{
  MenuBar mbMainMenuBar;
  Menu mnFile;
  Menu mnHelp;
  boolean fDBEmpty = true;
      public MainFrameWnd(String sTitle)
  {
    super(sTitle);
        setSize(400, 200);
    setBackground(Color.yellow);
    setForeground(Color.black);
        setLayout(new FlowLayout());
        mbMainMenuBar = new MenuBar();
    mnFile = new Menu("File");
        mnFile.add("New...");
    mnFile.add("View records...");
    mnFile.add("-");
    mnFile.add("Exit");
        mnHelp = new Menu("Help");
        mnHelp.add("Content");
    mnHelp.add("-");
    mnHelp.add("About");
        mbMainMenuBar.add(mnFile);
    mbMainMenuBar.add(mnHelp);
        setMenuBar(mbMainMenuBar);
  }
      public void paint(Graphics g)
  {
    g.setFont(new Font("Helvetica",
      Font.PLAIN, 12));
    g.drawString("Frame window", 10, 70);
    super.paint(g);
  }
      public boolean handleEvent(Event evt)
  {
    if(evt.id == Event.WINDOW_DESTROY)
    {
      setVisible(false);
      System.exit(0);
      return true;
    }
    else
      return super.handleEvent(evt);
  }
      public boolean action(Event evt,
   Object obj)
  {
    MenuItem mnItem;
    if(evt.target instanceof MenuItem)
    {
      mnItem = (MenuItem)evt.target;
          if(obj.equals("Exit"))
      {
        System.exit(0);
      }
          else if(obj.equals("New..."))
      {
    if(fDBEmpty)
     {
      SimpleDBMS db = new SimpleDBMS(
        "dbtest.idx", "dbtest.dat");
              db.AddRecord("Ivanov",   1000);
          db.AddRecord("Petrov",   2000);
      db.AddRecord("Sidoroff", 3000);
              db.close();
          fDBEmpty = false;
              MessageBox mbox;
          mbox = new MessageBox(
            "Database created",
            this, "Information", true);
      mbox.show();
    }
      }
          else if(obj.equals("View records..."))
      {
        SimpleDBMS db = new SimpleDBMS(
      "dbtest.idx", "dbtest.dat");
        String szRecords;

        szRecords =
          db.GetRecordByNumber(0) +
      db.GetRecordByNumber(1) +
      db.GetRecordByNumber(2);

            db.close();

            MessageBox mbox;
        mbox = new MessageBox(szRecords,
          this, "Database records", true);
    mbox.show();
      }

          else if(obj.equals("Content"))
      {
        MessageBox mbox;
        mbox = new MessageBox(
          "Item Content selected",
          this, "Dialog from Frame", true);
        mbox.show();
      }

          else if(obj.equals("About"))
      {
        MessageBox mbox;
        mbox = new MessageBox(
          "Item About selected",
          this, "Dialog from Frame", true);
        mbox.show();
      }
      else
        return false;
      return true;
    }
    return false;
  }
}
    class MessageBox extends Dialog
{
  Label lbMsg;
  Button btnOK;
      public MessageBox(String sMsg,
    Frame  parent, String  sTitle,
    boolean  modal)
  {
    super(parent, sTitle, modal);
    resize(300, 100);
    setLayout(new GridLayout(2, 1));
    lbMsg = new Label(sMsg, Label.CENTER);
    add(lbMsg);
    btnOK = new Button("OK");
    add(btnOK);
  }
      public boolean handleEvent(Event evt)
  {
    if(evt.id == Event.WINDOW_DESTROY)
    {
      dispose();
      return true;
    }
    else
      return super.handleEvent(evt);
  }
      public boolean action(Event evt,
   Object obj)
  {
    Button btn;
    if(evt.target instanceof Button)
    {
      btn = (Button)evt.target;
          if(evt.target.equals(btnOK))
      {
        dispose();
      }
      else
        return false;
      return true;
    }
    return false;
  }
}
    class SimpleDBMS
{
  RandomAccessFile idx;
  RandomAccessFile dat;
      long idxFilePointer = 0;
      public SimpleDBMS(String IndexFile,
   String DataFile)
  {
    try
    {
      idx = new RandomAccessFile(
        IndexFile, "rw");
      dat = new RandomAccessFile(
        DataFile, "rw");
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
      public void close()
  {
    try
    {
      idx.close();
      dat.close();
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
      public void AddRecord(String name,
     int account)
  {
    try
    {
      idx.seek(idx.length());
      dat.seek(dat.length());
          idxFilePointer = dat.getFilePointer();
          idx.writeLong(idxFilePointer);
          dat.writeBytes(name+ "\r\n");
      dat.writeInt(account);
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
      public String GetRecordByNumber(long nRec)
  {
    String sRecord = "<empty>";

        try
    {
      Integer account;
      String str = null;
          idx.seek(nRec * 8);
      idxFilePointer = idx.readLong();
      dat.seek(idxFilePointer);
          str = dat.readLine();
      account = new Integer(dat.readInt());
          sRecord = new String("> " +
       account + ", " + str);
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
    return sRecord;
  }
}

    Описание исходного текста приложения DirectFile

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

    Создание базы данных

    Когда пользователь выбирает из меню File строку New, соответствующий обработчик события создает базу данных, передавая конструктору имена файла индекса 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);

    После завершения работы с базой данных она закрывается методом close из класса SimpleDBMS:

    db.close();

    Просмотр записей базы данных

    При выборе строки View records из меню File приложение открывает файл базы данных:

    SimpleDBMS db = new SimpleDBMS(
  "dbtest.idx", "dbtest.dat");

    Затем оно извлекает три записи с номерами 0, 1 и 2, вызывая для этого метод GetRecordByNumber, также определенный в классе SimpleDBMS:

    String szRecords;

szRecords = db.GetRecordByNumber(0) +
  db.GetRecordByNumber(1) +
  db.GetRecordByNumber(2);

    Записи объединяются и сохраняются в переменной szRecords типа String. После этого база данных закрывается:

    db.close();

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

    MessageBox mbox;
mbox = new MessageBox(szRecords,
  this, "Database records", true);
mbox.show();

    Класс SimpleDBMS

    Рассмотрим теперь класс SimpleDBMS.

    В этом классе определено три поля с именами idx, dat и idxFilePointer, а также три метода.

    Поля класса SimpleDBMS

    Поля idx и dat являются объектами класса RandomAccessFile и представляют собой, соответственно, ссылки на файл индекса и файл данных. Поле idxFilePointer типа long используется как рабочее и хранит текущее смещение в файле.

    Конструктор класса SimpleDBMS

    Конструктор класса SimpleDBMS выглядит достаточно просто. Все, что он делает, - это создает два объекта класса RandomAccessFile, соответственно, для индекса и данных:

    idx = new RandomAccessFile(IndexFile, "rw");
dat = new RandomAccessFile(DataFile, "rw");

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

    Метод close

    Метод close закрывает файлы индекса и данных, вызывая метод close из класса RandomAccessFile:

    idx.close();
dat.close();

    Метод 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);

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

    Метод 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 возвращает в качестве извлеченной строки записи базы данных.


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

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

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

    Назад Вперед

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

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

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

    Для чего аплетам обращаться к файлам сервера Web?

    Таким аплетам можно найти множество применений.

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

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

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

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

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

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

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


Адрес IP и класс InetAddress

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Адрес IP и класс InetAddress

    Назад Вперед

    Прежде чем начинать создание сетевых приложений для Internet, вы должны разобраться с адресацией компьютеров в сети с протоколом TCP/IP, на базе которого построена сеть Internet. Здесь мы приведем самые необходимые для этого сведения.

    Все компьютеры, подключенные к сети TCP/IP, называются узлами (в оригинальной терминологии узел - это host). Каждый узел имеет в сети свой адрес IP, состоящий из четырех десятичных цифр в диапазоне от 0 до 255, разделенных символом "точка ", например:

    193.120.54.200

    Фактически адрес IP является 32-разрядным двоичным числом. Упомянутые числа представляют собой отдельные байты адеса IP.

    Так как работать с цифрами удобно лишь компьютеру, была придумана система доменных имен. При использовании этой системы адресам IP ставится в соответсвие так называемый доменный адрес, такой как, например, www.sun.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);

    Рассмотрим применение этих методов.

    Чтобы работать с адресами IP, прежде всего вы должны создать объект класса InetAddress. Эта процедура выполняется не с помощью оператора new, а с применением статических методов getLocalHost, getByName и getAllByName.

    Создание объекта класса InetAddress для локального узла

    Метод getLocalHost создает объект класса InetAddress для локального узла, то есть для той рабочей станции, на которой выполняется приложение Java. Так как этот метод статический, вы можете вызывать его, ссылаясь на имя класса InetAddress:

    InetAddress iaLocal;
iaLocal = InetAddress.getLocalHost();

    Создание объекта класса InetAddress для удаленного узла

    В том случае, если вас интересует удаленный узел сети Internet или корпоративной сети Intranet, вы можете создать для него объект класса InetAddress с помощью методов getByName или getAllByName. Первый из них возвращает адрес узла, а второй - массив всех адресов IP, связанных с данным узлом. Если узел с указанным именем не существует, при выполнении методов getByName и getAllByName возникает исключение UnknownHostException.

    Заметим, что методам getByName и getAllByName можно передавать не только имя узла, такое как, например, "sun.com", но и строку адреса IP в виде четырех десятичных чисел, разделенных точками.

    После создания объекта класса InetAddress для локального или удаленного узла вы можете использовать другие методы этого класса.

    Определение адреса IP

    Метод getAddress возвращает массив из чеырех байт адреса IP объекта. Байт с нулевым индексом этого массива содержит старший байт адреса IP.

    Метод toString возвращает текстовую строку, которая содержит имя узла, разделитель '/' и адрес IP в виде четырех десятичных чисел, разделенных точками.

    Определение имени узла

    С помощью метода getHostName вы можете определить имя узла, для которого был создан объект класса InetAddress.

    Сравнение адресов IP

    И, наконец, метод equals предназначен для сравнения адресов IP как объектов класса InetAddress.


Универсальный адрес ресурсов URL

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Универсальный адрес ресурсов 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.sun.com
http://157.23.12.101

    Необязательный параметр port задает номер порта для работы с сервером. По умолчанию для протокола HTTP используется порт с номером 80, однако для специализированных серверов Web это может быть и не так.

    Номер порта идентифицирует программу, работающую в узле сети TCP/IP и взаимодействующую с другими программами, расположенными на том же или на другом узле сети. Если вы разрабатываете программу, передающую данные через сеть TCP/IP с использованием, например, интерфейса сокетов Windows Sockets, то при создании канала связи с уделенным компьютером вы должны указать не только адрес IP, но и номер порта, который будет использован для передачи данных.

    Ниже мы показали, как нужно указывать в адресе URL номер порта:

    http://www.myspecial.srv/:82

    Теперь займемся параметром path, определяющем путь к объекту.

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

    Если в качестве адреса URL указать навигатору только доменное имя сервера, сервер перешлет навигатору свою главную страницу. Имя файла этой страницы зависит от сервера. Большинство серверов на базе операционной системы UNIX посылают по умолчанию файл документа с именем index.html. Другие серверы Web могут использовать для этой цели имя default.htm или какое-нибудь еще, определенное при установке сервера, например, home.html или home.htm.

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

    http://www.glasnet.ru/~frolov/index.html
http://www.dials.ccas.ru/frolov/home.htm

    Корневой каталог сервера Web обозначается символом /. В спецификации протокола HTTP сказано, что если путь не задан, то используется корневой каталог.


Класс URL в библиотеке классов Java

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form<

    Класс URL в библиотеке классов Java

    Назад Вперед

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

    Конструкторы класса 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, такие как протокол, имя узла, файла или номер порта.

    Методы класса URL

    Рассмотрим самые интересные методы, определенные в классе URL.

    Метод openStream

    Метод openStream позволяет создать входной поток для чтения файла ресурса, связанного с созданным объектом класса URL:

    public final InputStream openStream();

    Для выполнения операции чтения из созданного таким образом потока вы можете использовать метод read, определенный в классе InputStream (любую из его разновидностей).

    Данную пару методов (openStream из класса URL и read из класса InputStream) можно применить для решения задачи получения содержимого двоичного или текстового файла, хранящегося в одном из каталогов сервера Web. Сделав это, обычное приложение Java или аплет может выполнить локальную обработку полученного файла на компьютере удаленного пользователя.

    Метод getContent

    Очень интересен метод getConten. Этот метод определяет и получает содержимое сетевого ресурса, для которого создан объект URL:

    public final Object getContent();

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

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

    Данная проблема, тем не менее, решается очень просто - достаточно вместо метода getContent использовать описанную выше комбинацию методов openStream из класса URL и read из класса InputStream.

    Метод getHost

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

    public String getHost();

    Метод getFile

    Метод getFile позволяет получить информацию о файле, связанном с данным объектом URL:

    public String getFile();

    Метод getPort

    Метод getPortt предназначен для определения номера порта, на котором выполняется связь для объекта URL:

    public int getPort();

    Метод getProtocol

    С помощью метода getProtocol вы можете определить протокол, с использованием которого установлено соединение с ресурсом, заданным объектом URL:

    public String getProtocol();

    Метод getRef

    Метод getRef возвращает текстовую строку ссылки на ресурс, соответствующий данному объекту URL:

    public String getRef();

    Метод hashCode

    Метод hashCode возвращает хэш-код объекта URL:

    public int hashCode();

    Метод sameFile

    С помощью метода sameFile вы можете определить, ссылаются ли два объекта класса URL на один и тот же ресурс, или нет:

    public boolean sameFile(URL other);

    Если объекты ссылаются на один и тот же ресурс, метод sameFile возвращает значение true, если нет - false.

    Метод equals

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

    public boolean equals(Object obj);

    Если адреса URL идентичны, метод equals возвращает значение true, если нет - значение false.

    Метод toExternalForm

    Метод toExternalForm возвращает текстовую строку внешнего представления адреса URL, определенного данным объектом класса URL:

    public String toExternalForm();

    Метод toString

    Метод toString возвращает текстовую строку, представляющую данный объект класса URL:

    public String toString();

    Метод openConnection

    Метод openConnection предназначен для создания канала между приложением и сетевым ресурсом, представленным объектом класса URL:

    public URLConnection openConnection();

    Если вы создаете приложение, которое позволяет читать из каталогов сервера Web текстовые или двоичные файлы, можно создать поток методом openStream или получить содержимое текстового ресурса методом getContent.

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

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


Передача данных с использованием сокетов

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Передача данных с использованием сокетов

    Назад Вперед

    В библиотеке классов Java есть очень удобное средство, с помощью которых можно организовать взаимодействие между приложениями Java и аплетами, работающими как на одном и том же, так и на разных узлах сети TCP/IP. Это средство, родившееся в мире операционной системы UNIX, - так называемые сокеты (sockets).

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

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

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

    С адресом узла IP вы уже сталкивались. Номер порта служит для идентификации приложения. Заметим, что существуют так называемые "хорошо известные" (well known) номера портов, зарезервированные для различных приложений. Например, порт с номером 80 зарезервирован для использования серверами Web при обмене данными через протокол HTTP.

    Что же касается типов сокетов, то их два - потоковые и датаграммные.

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

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

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

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

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


Работа с потоковыми сокетами

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Работа с потоковыми сокетами

    Назад Вперед

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

    Рассмотрим этот процесс в деталях.

    Инициализация сервера

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

    Первое, что должно сделать серверное приложение, это создать объект класса 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, который мы рассмотрим позже.

    Инициализация клиента

    Процесс инициализации клиентского приложения выглядит весьма просто. Клиент должен просто создать сокет как объект класса Socket, указав адрес IP серверного приложения и номер порта, используемого сервером:

    Socket s;
s = new Socket("localhost",9999);

    Здесь в качестве адреса IP мы указали специальный адрес localhost, предназначенный для тестирования сетевых приложений, а в качестве номера порта - ззначение 9999, использованное сервером.

    Теперь можно создавать входной и выходной потоки. На стороне клиента эта операция выполняется точно также, как и на стороне сервера:

    InputStream is;
OutputStream os;
is = s.getInputStream();
os = s.getOutputStream();

    Передача данных между клиентом и сервером

    После того как серверное и клиентское приложения создали потоки для приема и передачи данных, оба этих приложения могут читать и писать в канал данных, вызывая методы 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 вы можете создать буферизованные потоки и потоки для передачи форматированных данных, о которых мы рассказывали раньше.

    Завершение работы сервера и клиента

    После завершения передачи данных вы должны закрыть потоки, вызвав метод close:

    is.close();
os.close();

    Когда канал передачи данных больше не нужен, сервер и клиент должны закрыть сокет, вызвав метод close, определенный в классе Socket:

    s.close();

    Серверное приложение, кроме того, должно закрыть соединение, вызвав метод close для объекта класса ServerSocket:

    ss.close();


Класс Socket

Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Класс 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.

    Методы класса 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();


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

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

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

    Назад Вперед

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

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

    Канал, а также входные и выходные потоки создавать не нужно. Данные передаются и принимаются методами send и receive, определенными в классе DatagramSocket.

    Класс 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 выполняется только для объектов, находящихся в оперативной памяти. Такие объекты, как потоки и сокеты, вы должны закрывать после использования самостоятельно.

    Класс 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.


Связь приложений Java с расширениями сервера Web

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Связь приложений Java с расширениями сервера Web

    Назад Вперед

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

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

    Взаимодействие приложения Java и расширения сервера Web

    Методика организации взаимодействия приложений Java и расширений сервера Web основана на применении классов URL и URLConnection.

    Приложение Java, желающее работать с расширением сервера Web, создает объект класса URL для программы расширения (то есть для исполняемого модуля расширения CGI или библиотеки динамической компоновки DLL расширения ISAPI).

    Далее приложение получает ссылку на канал передачи данных с этим расширением как объекта класса URLConnection. Затем, пользуясь методами getOutputStream и getInputStream из класса URLConnection, приложение создает с расширением сервера Web выходной и входной канал передачи данных.

    Когда данные передаются приложением в выходной канал, созданный подобным образом, он попадает в стандартный поток ввода приложения CGI, как будто бы данные пришли методом POST из формы, определенной в документе HTML.

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

    На рис. 1 показаны потоки данных для описанной выше схемы взаимодействия приложения Java и расширения сервреа Web с интерфейсом CGI.

    pic01.gif (4413 bytes)

    Рис. 1. Взаимодействие приложения Java с расширением сервера Web на базе интерфейса CGI

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

    Класс 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();

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

    • setDefaultUseCaches

    Включение или отключение кэширования по умолчанию

    public void setDefaultUseCaches(
  boolean defaultusecaches);
    • setUseCaches

    Включение или отключение кэширования

    public void setUseCaches(boolean usecaches);
    • setDoInput

    Возможность использования потока для ввода

    public void setDoInput(boolean doinput);
    • setDoOutput

    Возможность использования потока для вывода

    public void setDoOutput(boolean dooutput);
    • setIfModifiedSince

    Установка даты модификации документа

    public void setIfModifiedSince(
  long ifmodifiedsince);

    В классе URLConnection есть методы, позволяющие определить значения параметров, установленных только что описанными методами:

    public boolean getDefaultUseCaches();
public boolean getUseCaches();
public boolean getDoInput();
public boolean getDoOutput();
public long getIfModifiedSince();

    Определенный интерес могут представлять методы, предназначенные для извлечения информации из заголовка протокола HTTP:

    • getContentEncoding

    Метод возвращает содержимое заголовка content-encoding (кодировка ресурса, на который ссылается URL)

    public String getContentEncoding();
    • getContentLength

    Метод возвращает содержимое заголовка content-length (размер документа)

    public int getContentLength();
    • getContentType

    Метод возвращает содержимое заголовка content-type (тип содержимого)

    public String getContentType();
    • getDate

    Метод возвращает содержимое заголовка date (дата посылки ресурса в секундах с 1 января 1970 года)

    public long getDate();
    • getLastModified

    Метод возвращает содержимое заголовка last-modified (дата изменения ресурса в секундах с 1 января 1970 года)

    public long getLastModified();
    • getExpiration

    Метод возвращает содержимое заголовка expires (дата устаревания ресурса в секундах с 1 января 1970 года)

    public long getExpiration();

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

Sun Microsystems Inc. Corporate Information Our partners News and Events Search on site Java Computing


Аплет ShowChart

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Аплет ShowChart

    Назад Вперед

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

    10,20,5,35,11,10,3,6,80,10,20,5,35,11,10,3,6,80

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

    pic02.gif (3920 bytes)

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

    Файл исходных данных занимает всего 49 байт, поэтому он передается по сети очень быстро. Если бы мы передавали графическое изображение этой диаграммы, статическое или динамическое, подготовленное, например, расширением сервера CGI или ISAPI, объем передаваемых по сети данных был бы намного больше.

    Исходные тексты аплета ShowChart

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

    Листинг 1. Файл ShowChart.java

    import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;
    public class ShowChart extends Applet
{
  URL SrcURL;
  Object URLContent;
  int errno = 0;
  String str;
  byte buf[] = new byte[200];
      public String getAppletInfo()
  {
    return "Name: ShowChart";
  }
      public void init()
  {
    try
    {
      SrcURL = new URL(
        "http://frolov/chart.txt");
      try
      {
        InputStream is = SrcURL.openStream();
        is.read(buf);
        str = new String(buf, 0);
      }
      catch (IOException ioe)
      {
        showStatus("read exception");
        errno = 1;
      }
    }
    catch (MalformedURLException uex)
    {
      showStatus(
        "MalformedURLException exception");
      errno = 2;
    }
  }
      public void paint(Graphics g)
  {
    Integer AngleFromChart = new Integer(0);
    int PrevAngle = 0;
    int rColor, gColor, bColor;
    Dimension dimAppWndDimension = getSize();

        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);
        showStatus(str);
    StringTokenizer st =
       new StringTokenizer(str, ",\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));
          String angle =
        (String)st.nextElement();
      AngleFromChart = new Integer(angle) ;
      g.fillArc(0, 0, 200, 200,
        PrevAngle,
        AngleFromChart.intValue());
      PrevAngle += AngleFromChart.intValue();
    }
  }
}

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

    Листинг 2. Файл ShowChart.tmp.html

    <applet
  name="ShowChart"
  code="ShowChart"
  codebase=
  "file:/e:/Sun/Articles/vol12/src/ShowChart"
  width="200"
  height="200"
  align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
  <hr>If your browser recognized
the applet tag,
you would see an applet here.
  <hr>
</applet>

    Описание исходного текста аплета ShowChart

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

    Поля класса ShowChart

    В классе ShowChart определены пять полей.

    URL SrcURL;
Object URLContent;
int errno = 0;
String str;
byte buf[] = new byte[200];

    Поле SrcURL класса URL хранит адрес URL файла исходных данных для круговой диаграммы. В поле URLContent типа Object будет переписано содержимое этого файла. В поле errno хранится текущий код ошибки, если она возникла, или нулевое значение, если все операции были выполнены без ошибок.

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

    Метод init

    Во время инициализации метод init создает объект класса URL для файла исходных данных:

    SrcURL = new URL("http://frolov/chart.txt");

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

    Далее для нашего объекта URL мы создаем поток ввода и получаем содержимое файла (то есть исходные данные для построения диаграммы):

    InputStream is = SrcURL.openStream();
is.read(buf);

    Принятые данные записываются в буфер buf и затем преобразуются к типу String с помощью соответствующего конструктора:

    str = new String(buf, 0);

    Если при создании объекта класса URL возникло исключение, метод init записывает в поле errno код ошибки, равный 2, записывая при этом в строку состояния браузера сообщение "MalformedURLException exception".

    В том случае, когда объект класса URL создан успешно, а исключение возникло в процессе чтения содержимого файла, в поле errno записывается значение 1, а в строку состояния браузера - сообщение "read exception".

    Метод paint

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

    showStatus(sChart);

    Далее создается разборщик строки исходных данных:

    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));

    С помощью метода nextElement мы получаем очередное значение угла сектора и сохраняем его в переменной angle:

    String angle = (String)st.nextElement();

    Далее с помощью конструктора класса Integer это значение преобразуется в численное:

    AngleFromChart = new Integer(angle);

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

    g.fillArc(0, 0, 200, 200,
  PrevAngle, AngleFromChart.intValue());

    В качестве начального значения угла сектора используется значение из переменной PrevAngle. Сразу после инициализации в эту переменную записывается нулевое значение.

    Конечный угол сектора задается как AngleFromChart.intValue(), то есть указывается значение, полученное из принятого по сети файла исходных данных.

    После завершения рисования очередного сектора круговой диаграммы начальное значение PrevAngle увеличивается на величину угла нарисованного сектора:

    PrevAngle += AngleFromChart.intValue();


Приложения SocketServ и SocketClient

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Приложения SocketServ и SocketClient

    Назад Вперед

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

    Приложение SocketServ выводит на консоль строку "Socket Server Application" и затем переходит в состояние ожидания соединения с клиентским приложением SocketClient.

    Приложение SocketClient устанавливает соединение с сервером SocketServ, используя потоковый сокет с номером 9999 (этот номер выбран нами произвольно). Далее клиентское приложение выводит на свою консоль приглашение для ввода строк. Введенные строки отображаются на консоли и передаются серверному приложению. Сервер, получив строку, отображает ее в своем окне и посылает обратно клиенту. Клиент выводит полученную от сервера строку на консоли.

    Когда пользователь вводит строку "quit", цикл ввода и передачи строк завершается.

    Весь процесс показан на рис. 3 и 4.

    pic03.gif (4444 bytes)

    Рис. 3. Окно клиентского приложения

    pic04.gif (3495 bytes)

    Рис. 4. Окно серверного приложения

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

    Исходный текст серверного приложения SocketServ

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

    Листинг 3. Файл SocketServ.java

    import java.io.*;
import java.net.*;
import java.util.*;
    public class SocketServ
{
  public static void main(String args[])
  {
    byte bKbdInput[] = new byte[256];
    ServerSocket ss;
    Socket s;
    InputStream is;
    OutputStream os;
        try
    {
      System.out.println(
        "Socket Server Application");
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
        try
    {
      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 <Enter> to terminate
 application...");
      System.in.read(bKbdInput);
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
}

    Описание исходного текста серверного приложения 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();

    Исходный текст клиентского приложения SocketClient

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

    Листинг 4. Файл SocketClient.java

    import java.io.*;
import java.net.*;
import java.util.*;
    public class SocketClient
{
  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)
        {
          str = new String(bKbdInput, 0);
              StringTokenizer st;
          st   = new StringTokenizer(
            str, "\r\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, "\r\n");
          str = new String(
              (String)st.nextElement());
          System.out.println(">> " + str);
              if(str.equals("quit"))
            break;
        }
      }
      is.close();
      os.close();
      s.close();
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
        try
    {
      System.out.println(
        "Press <Enter> to " +
         "terminate application...");
      System.in.read(bKbdInput);
    }
    catch(Exception ioe)
    {
      System.out.println(ioe.toString());
    }
  }
}

    Описание исходного текста клиентского приложения 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.

    Далее если пользователь ввел строку, а не просто нажал на клавишу <Enter>, эта строка отображается на консоли и передается серверу:

    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();


Аплет Form

Сетевые приложения
Адрес IP
Адрес URL
Класс URL
Сокеты
Потоковые сокеты
Класс Socket
Датаграммные сокеты
Связь с Web
Аплет ShowChart
Приложения SocketServ и SocketClient
Аплет Form

    Аплет Form

    Назад Вперед

    На примере аплета Form мы покажем, как приложения Java могут взаимодействовать с расширениями сервера Web, такими как программы CGI или приложения ISAPI.

    В окне нашего аплета находится форма, содержащая два однострочных поля редактирования, кнопку и многострочное поле редактирования (рис. 5).

    pic05.gif (4332 bytes)

    Рис. 5. Окно аплета Form

    Эта форма предназначена для добавления записей в базу данных, содержащую электронные почтовые адреса. Заполнив поля имени и адреса E-Mail, пользователь должен нажать кнопку Send. При этом введенная информация будет передана расширению сервера CGI, который запишет ее в базу данных, а затем отправит обратно аплету. Сохраненные записи, полученные от программы CGI, аплет FORM отобразит в многострочном поле редактирования, как это показано на рис. 5.

    Исходные тексты аплета Form

    Исходные тексты аплета Form представлены в листинге 5.

    Листинг 5. Файл Form.java

    import java.applet.*;
import java.awt.*;
import java.net.*;
import java.io.*;
import java.util.*;
    public class Form extends Applet
  implements Runnable
{
  private Thread m_store = null;
      TextField txtName;
  TextField txtEMail;
  TextArea  txta;
  Button btnGetText;
      public void init()
  {
    Label lbName;
    Label lbEMail;
    Label lbPress;
        lbName = new Label("Enter your name:");
    lbEMail = new Label(
      "Enter your E-Mail address:");
        add(lbName);
        txtName = new TextField("Your name", 40);
    add(txtName);
        add(lbEMail);
        txtEMail =
      new TextField("your@email", 40);
    add(txtEMail);
        btnGetText = new Button("Send!");
    add(btnGetText);
        txta = new TextArea(8, 65);
    add(txta);
        setBackground(Color.yellow);
  }
      public void paint(Graphics g)
  {
    setBackground(Color.yellow);
        Dimension dimAppWndDimension = getSize();
    g.setColor(Color.black);
    g.drawRect(0, 0,
      dimAppWndDimension.width  - 1,
      dimAppWndDimension.height - 1);
  }
      public boolean action(Event evt, Object obj)
  {
    Button btn;
        if(evt.target instanceof Button)
    {
      btn = (Button)evt.target;
          if(evt.target.equals(btnGetText))
      {
        startTransaction();
      }
      else
        return false;
      return true;
    }
    return false;
  }
      void startTransaction()
  {
    m_store = new Thread(this);
    m_store.start();
  }
      public void stop()
  {
    if (m_store != null)
    {
      m_store.stop();
      m_store = null;
    }
  }
      public void run()
  {
    URL u;
    URLConnection c;
    PrintStream ps;
    DataInputStream is;
        try
    {
      String szSourceStr =
        txtName.getText() +
    ", " + txtEMail.getText();
          String szReceived;
      String szURL =
      "http://frolov/scripts/store.exe";
          u = new URL(szURL);
      c = u.openConnection();
          ps = new PrintStream(
        c.getOutputStream());
      ps.println(szSourceStr);
      ps.close();
          is = new DataInputStream(
        c.getInputStream());
          szReceived = is.readLine();
      is.close();
          txta.appendText(szReceived + "\r\n");
      repaint();
    }
    catch (Exception ioe)
    {
      showStatus(ioe.toString());
      stop();
    }
  }
}

    Исходный текст документа HTML, который был подготовлен для нас системой Java Workshop, мы немного отредактировали, изменив параметр CODEBASE (листинг 6).

    Листинг 6. Файл Form.tmp.html

    <applet name="Form"
  code="Form.class"
  codebase="http://frolov/"
  width="500"
  height="200"
  align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
   <hr>If your browser
recognized the applet tag,
you would see an applet here.<hr>
</applet>

    В этом параметре следует указать путь к каталогу, в котором располагается байт-код аплета.

    Описание исходныех текстов аплета Form

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

    Когда пользователь заполняет форму и нажимает кнопку Send, обработчик соответствующего события вызывает метод startTransaction, запускающий процесс обмена данными с расширением сервера Web:

    if(evt.target.equals(btnGetText))
{
  startTransaction();
}

    Метод startTransaction, определенный в нашем приложении, создает и запускает на выполнение поток, который и будет взаимодействовать с программой CGI:

    void startTransaction()
{
  m_store = new Thread(this);
  m_store.start();
}

    При этом в качестве отдельного потока, работающего одновременно с кодом аплета, выступает метод run. Именно в нем сосредоточена вся логика обмена данными с сервером Web.

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

    URL u;
URLConnection c;
PrintStream ps;
DataInputStream is;
    try
{
  . . .
}
catch (Exception ioe)
{
  showStatus(ioe.toString());
  stop();
}

    Название возникшего исключения будет отображено в строке состояния браузера.

    Теперь о том, что делает метод run после получения управления.

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

    String szSourceStr =
   txtName.getText() + ", " +
   txtEMail.getText();

    В строке szURL находится адрес URL программы CGI:

    String szURL =
  "http://frolov/scripts/store.exe";

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

    На следующем этапе метод run создает для программы CGI объект класса URL и открывает с ним соединение:

    u = new URL(szURL);
c = u.openConnection();

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

    ps = new PrintStream(c.getOutputStream());
ps.println(szSourceStr);
ps.close();

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

    Сделав это, программа CGI запишет в стандартный выходной поток строку ответа, которую необходимо прочитать в методе run нашего аплета. Для этого мы открываем входной поток, создаем на его основе форматированный входной поток данных, читаем одну строку текста и закрываем входной поток:

    is = new DataInputStream(c.getInputStream());
String szReceived;
szReceived = is.readLine();
is.close();

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

    txta.appendText(szReceived + "\r\n");
repaint();

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

    Исходный текст программы CGI store.exe

    Исходный текст программы CGI store.exe очень прост и показан в листинге 7.

    Листинг 7. Файл store.c

    #include <windows.h>
#include <tchar.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
    void main(int argc, char *argv[])
{
  int   nInDatasize;
  char  * szMethod;
  char  szBuf[2000];
      FILE *fDatabase;
  CRITICAL_SECTION csAddRecord;
      szMethod = getenv("REQUEST_METHOD");
  if(!strcmp(szMethod, "POST"));
  {
    nInDatasize = atoi(
      getenv("CONTENT_LENGTH"));
        fread(szBuf, nInDatasize, 1, stdin);
    szBuf[nInDatasize] = '\0';
        InitializeCriticalSection(&csAddRecord);
    EnterCriticalSection(&csAddRecord);
        fDatabase =
      fopen("c:\\EMAIL.DAT", "a+");
    if(fDatabase != NULL)
    {
      fputs(szBuf, fDatabase);
      fclose(fDatabase);
    }
        LeaveCriticalSection(&csAddRecord);
    DeleteCriticalSection(&csAddRecord);
        printf(
      "Content-type: text/plain\r\n\r\n");
    printf("Stored information: %s", szBuf);
  }
}

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

    Свою работу программа CGI начинает с анализа переменной среды REQUEST_METHOD. Убедившись, что при запуске программы ей передали данные методом POST, программа определяет размер этих данных исходя из содержимого переменной среды CONTENT_LENGTH.

    Далее программа считывает соответствующее количество байт данных из стандартного потока ввода, записывает их в файл. Затем, после добавления заголовка "Stored information:", программа CGI записывает полученную строку в стандартный выходной поток, передавая ее таким образом аплету Form.

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

    Еще одно замечание касается пути к файлу, который в нашем случае создается в корневом каталоге диска C:. При установке программы CGI на сервер вам необходимо обеспечить доступ на запись к каталогу, в котором располагается файл, для удаленных пользователей. О том, как это сделать, вы можете узнать из документации на ваш сервер Web.


    Растровые изображения и анимация

    Назад Вперед

    Одно из наиболее распространенный применений аплетов связано с рисованием простых или анимированных растровых изображений. На серверах Web изображения обычно хранятся в форматах GIF или JPEG. Оба эти формата обеспечивают сжатие изображения, что весьма актуально из-за невысокой скорости передачи данных в сети Internet.

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

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


    Загрузка и рисование растрового изображения

    Назад Вперед

    Загрузка растрового изображения из файла выполняется очень просто - с помощью метода 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.

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

    Параметр bgcolor задает цвет фона, на котором будет нарисовано изображение. Как вы, вероятно, знаете, изображения 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 запускает процесс загрузки и рисования изображения, а затем, не дожидаясь его завершения, возвращает управление. Так как загрузка файла изображения по сети может отнять немало времени, она выполняется асинхронно в отдельной задаче.


Класс Image
Ожидание загрузки
Видео
Аплет CDRotation

    Класс 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();

    Он освобождает ресурсы, занятые изображением.


    Ожидание загрузки изображений

    Назад Вперед

    Как мы уже говорили в наших предыдущих статьях, загрузка изображений из сети Internet - длительный процесс, который в среднем идет со скоростью 1 Кбайт в секунду. Поэтому изображения загружаются навигатором в отдельной задаче. При этом метод getImage только создает объект класса Image, а метод drawImage инициирует загрузку изображения и рисует его. Причем если файл изображения имеет большую длину, он будет появляться в окне аплета постепенно по мере загрузки.

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

    Есть ли способ определить, когда изображение будет загружено полностью?

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

    Применение класса MediaTracker

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

    Как это сделать?

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

    Создание объекта класса MediaTracker

    Объект класса MediaTracker создается следующим образом:

    MediaTracker mt;
mt = new MediaTracker(this);

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

    Добавление изображений в объект класса 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 передается ссылка на изображение, загрузку которого необходимо отслеживать, а в качестве второго - идентификатор, который можно будет использовать в процессе отслеживания. Если все, что вам нужно, это дождаться окончания загрузки изображений, то для второго параметра вы можете указать нулевое значение.

    Ожидание загрузки добавленных изображений

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

    try
{
  mt.waitForAll();
}
catch (InterruptedException ex)
{
}

    Обратите внимание, что метод waitForAll может создавать исключение InterruptedException. Это исключение возникает, если по какой-либо причине процесс ожидания прерывается.

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

    Другие методы класса 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 возвращают массив объектов, при ожидании загрузки которых произошла ошибка. Первый из этих методов возвращает массив для всех отслеживаемых объектов, второй - только объектов из заданной группы.

    Применение интерфейса ImageObserver

    Второй способ ожидания завершения процесса загрузки изображений связан с интерфейсом 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 метода imageUpdateimageUpdate
PROPERTIES | Стали доступны свойства изображения, которые можно получить методом getProperty класса Image. В нашей книге мы опустили описание этого метода
SOMEBITS | Стали доступны биты изображения для рисования в масштабе. Через параметры x, y, h и w передаются координаты и размеры прямоугольной области, которая ограничивает загруженную часть изображения
FRAMEBITS | Загружен очередной фрейм изображения, состоящего из нескольких фреймов. Параметры x, y, h и w следует игнорировать
ALLBITS | Изображение загружено полностью. Параметры x, y, h и w следует игнорировать
ERROR | При загрузке произошла ошибка
ABORT | Загрузка изображения была прервана или отменена

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

    Если вам нужно только дождаться завершения процесса загрузки, достаточно использовать флаг ALLBITS. Для проверки ошибок воспользуйтесь флагами ERROR и ABORT.


    Видео в окне аплета

    Назад Вперед

    Наиболее динамичные страницы сервера Web содержат анимационные изображения в виде небольших видеофильмов. Вы можете подготовить видеофильм как файл AVI или как многосекционный файл GIF.

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

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

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

    Аплеты Java предоставляют вам еще одну возможность отображения небольших видеофильмов на страницах сервера Web.

    Для реализации этой возможности вы должны подготовить и разместить в одном из каталогов сервера Web файлы отдельных кадров видеофильма в формате GIF или JPEG.

    Аплет Java должен загрузить эти изображения, дождавшись окончания процесса загрузки, что можно сделать либо при помощи рассмотренного в этой главе класса MediaTracker либо при помощи интерфейса ImageObserver.

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

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

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


Аплет CDRotation

    Аплет CDRotation

    Назад Вперед

    В этом разделе мы расскажем об аплете CDRotation, в окне которого вращается компакт-диск.

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

    pic01.gif (3268 bytes)

    Рис. 1. Изображение вращающегося компакт-диска в окне аплета CDRotation

    Исходные тексты приложения

    Главный файл исходных текстов приложения CDRotation представлен в листинге 1.

    Листинг 1. Файл CDRotation.java

    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;
      public String getAppletInfo()
  {
    return "Name: CDRotation";
  }
      private void displayImage(Graphics g)
  {
    if (!m_fAllLoaded)
      return;
        g.drawImage(m_Images[m_nCurrImage],
       (size().width - m_nImgWidth)   / 2,
       (size().height - m_nImgHeight) / 2,
        null);
        g.drawString(
      (new Integer(m_nCurrImage)).toString(),
      (size().width - m_nImgWidth)  /2,
     ((size().height - m_nImgHeight)/2)+
     10);
  }
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension = size();
    g.setColor(Color.white);
    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_fAllLoaded)
    {
      displayImage(g);
    }
        else
      g.drawString("Please, wait...",
        10, dimAppWndDimension.height / 2);
  }
      public void start()
  {
    if (m_CDRotation == null)
    {
      m_CDRotation = new Thread(this);
      m_CDRotation.start();
    }
  }
      public void stop()
  {
    if (m_CDRotation != null)
    {
      m_CDRotation.stop();
      m_CDRotation = null;
    }
  }
      public void run()
  {
    m_nCurrImage = 0;
        if (!m_fAllLoaded)
    {
      repaint();
      m_Graphics = getGraphics();
          m_Images   = new Image[NUM_IMAGES];
          MediaTracker tracker =
         new MediaTracker(this);
          String strImage;
          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);
      }
          try
      {
    tracker.waitForAll();
        m_fAllLoaded = !tracker.isErrorAny();
      }
      catch (InterruptedException e)
      {
      }
          if (!m_fAllLoaded)
      {
        stop();
        m_Graphics.drawString(
          "Load error", 10,
           size().height / 2);
            return;
      }
          m_nImgWidth  =
        m_Images[0].getWidth(this);
      m_nImgHeight =
         m_Images[0].getHeight(this);
    }
        repaint();
        while (true)
    {
      try
      {
        displayImage(m_Graphics);
    m_nCurrImage++;
        if(m_nCurrImage == NUM_IMAGES)
    m_nCurrImage = 0;
            Thread.sleep(30);
      }
          catch (InterruptedException e)
      {
    stop();
      }
    }
  }
}

    Листинг 2 содержит исходный текст документа HTML, созданного для аплета CDRotation.

    Листинг 2. Файл CDRotation.tmp.html

    <applet name="CDRotation"
  code="CDRotation"
  codebase=
"file:/e:/sun/articles/vol13/src/CDRotation"
  width="500"
  height="600"
  align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
</applet>

    Описание исходных текстов

    Рассмотрим наиболее важные методы нашего аплета.

    Метод start

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

    if (m_CDRotation == null)
{
  m_CDRotation = new Thread(this);
  m_CDRotation.start();
}

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

    Метод stop

    Метод stop останавливает работу потока, когда окно аплета исчезает с экрана:

    if(m_CDRotation != null)
{
  m_CDRotation.stop();
  m_CDRotation = null;
}

    Для остановки вызывается метод stop.

    Метод paint

    Сразу после получения управления, метод paint закрашивает окно аплета белым цветом и рисует вокруг него черную рамку.

    Затем метод проверяет содержимое флага m_fAllLoaded. Этот флаг установлен в значение true, когда все кадры видеофильма загружены и сброшен в значение false, когда загрузка кадров еще не завершена. Последняя ситуация возникает всегда при первом вызове метода paint.

    Если все изображения загружены, метод paint вызывает метод displayImage, определенный в нашем приложении:

    if(m_fAllLoaded)
{
  displayImage(g);
}

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

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

    else
  g.drawString("Please, wait...",
    10, dimAppWndDimension.height / 2);

    Метод run

    Метод 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 (и, следовательно, работа созданного для него потока) заканчивается:

    if(!m_fAllLoaded)
{
  stop();
  m_Graphics.drawString(
    "Load error",
    10, size().height / 2);
  return;
}

    В случае удачной загрузки всех кадров метод run получает ширину и высоту первого кадра видеофильма и сохраняет эти значения в переменных m_nImgWidth и m_nImgHeight:

    m_nImgWidth  =
  m_Images[0].getWidth(this);
m_nImgHeight =
  m_Images[0].getHeight(this);

    Далее окно аплета перерисовывается:

    repaint();

    При этом метод paint отображает в окне аплета первый кадр видеофильма.

    На следующем этапе работы метода run запускается цикл отображения кадров фильма:

    while (true)
{
  try
  {
    displayImage(m_Graphics);
    m_nCurrImage++;
    if(m_nCurrImage == NUM_IMAGES)
      m_nCurrImage = 0;
    Thread.sleep(30);
  }
  catch (InterruptedException e)
  {
    stop();
  }
}

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

    Между отображением кадров выполняется задержка величиной 30 миллисекунд.

    Метод 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);


Звук в аплетах Java
Загрузка и проигрывание
Аплет PlayClip

    Звук в аплетах Java

    Назад Вперед

    Нельзя сказать, что звуковые возможности аплетов Java чрезмерно велики. Скорее наоборот, они минимальны. Тем не менее, аплеты могут проигрывать звуковые клипы, записанные в файлах формата AU, который пришел из мира компьютеров фирмы Sun.

    Сказанное, однако, не означает, что если у вас нет рабочей станции Sun, то вы не сможете озвучить свои аплеты. Во-первых, в сети Internet можно найти много готовых звуковых файлов AU, а во-вторых, там же есть программы для преобразования форматов звуковых файлов. Одну из таких условно-бесплатных программ, которая называется GoldWave, вы можете загрузить с сервера ftp.winsite.com.


Загрузка и проигрывание звуковых файлов
Аплет PlayClip

    Загрузка и проигрывание звуковых файлов

    Назад Вперед

    Работа со звуковыми файлами во многом напоминает работу с растровыми графическими файлами. Вначале вы должны получить ссылку на интерфейс 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, как нетрудно догадаться из его названия, останавливает проигрывание звукового файла, как однократное, так и выполняемое в цикле.


Аплет PlayClip

    Аплет PlayClip

    Назад

    Аплет PlayClip демонстрирует использование интерфейса AudioClip. В его окне (рис. 1) имеются три кнопки с названиями Play, Loop и Stop.

    pic01.gif (1685 bytes)

    Рис. 1. Окно аплета PlayClip

    Сразу после запуска аплета кнопка Stop находится в заблокированном состоянии. Если нажать кнопку Play или Loop, начнется, соответственно, однократное проигрывание или проигрывание в цикле файла с именем kaas.au, распложенного в том же каталоге, что и двоичный файл аплета PlayClip.

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

    Исходные тексты приложения

    Основной файл исходных текстов приложения приведен в листинге 1.

    Листинг 1. Файл PlayClip.java

    import java.applet.*;
import java.awt.*;
    public class PlayClip 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;
      public String getAppletInfo()
  {
    return "Name: PlayClip";
  }
      public String[][] getParameterInfo()
  {
    String[][] info =
    {
      {
    PARAM_ClipName,
    "String",
    "Audioclip filename"
      },
    };
    return info;
  }
      public void init()
  {
    String param;
    param = getParameter(PARAM_ClipName);
    if (param != null)
      m_ClipName = param;
        btPlay = new Button("Play");
    btLoop = new Button("Loop");
    btStop = new Button("Stop");
        btStop.disable();
        add(btPlay);
    add(btLoop);
    add(btStop);
        auClip = this.getAudioClip(getCodeBase(),
      m_ClipName);
  }
      public boolean action(Event evt,
    Object obj)
  {
    Button btn;
        if(evt.target instanceof Button)
    {
      btn = (Button)evt.target;
          if(evt.target.equals(btPlay))
      {
        auClip.play();
        btStop.enable();
      }
          else if(evt.target.equals(btLoop))
      {
        auClip.loop();
        fLoopPlay = true;
        btStop.enable();
      }
          else if(evt.target.equals(btStop))
      {
        auClip.stop();
        fLoopPlay = false;
        btStop.disable();
      }
          else
      {
        return false;
      }
          return true;
    }
        return false;
  }
      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);
  }
      public void start()
  {
    if(fLoopPlay)
      auClip.loop();
  }
      public void stop()
  {
    if(fLoopPlay)
      auClip.stop();
  }
}

    В листинге 2 вы найдете исходный текст документа HTML, созданного автоматически для нашего приложения системой Java WorkShop.

    Листинг 2. Файл PlayClip.tmp.html

    <applet name="PlayClip"
  code="PlayClip"
  codebase=
"file:/e:/sun/articles/vol14/src/PlayClip"
 width="200"
 height="100"
 align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
<param name="ClipName" value="kaas.au">
<hr>
If your browser recognized the applet tag,
 you would see an applet here.
<hr>
</applet>

    Описание исходного текста

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

    Поля класса PlayClip

    В поле m_ClipName хранится имя звукового файла, которое передается через параметр ClipName из документа HTML. По умолчанию для этого параметра используется значение kaas.au.

    Строка PARAM_ClipName хранит имя указанного выше параметра.

    Ссылка на интерфейс AudioClip хранится в поле auClip:

    AudioClip auClip;

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

    Button btPlay;
Button btLoop;
Button btStop;

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

    Метод getParameterInfo

    Метод getParameterInfo возвращает описание единственного параметра нашего аплета, через который передается имя звукового файла.

    Метод 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);

    Последнее, что делает метод init перед тем как возвратить управление, это получение ссылки на интерфейс AudioClip:

    auClip = this.getAudioClip(
  getCodeBase(),m_ClipName);

    Адрес URL каталога, в котором расположен аплет, определяется с помощью метода getCodeBase, о котором мы говорили в предыдущей главе.

    Метод 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 блокируется.

    Метод start

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

    Наша реализация метода start возобновляет циклическое проигрывание, если оно выполнялось, когда пользователь покинул страницу с аплетом:

    if(fLoopPlay)
  auClip.loop();

    Метод stop

    Если пользователь запустил проигрывание звукового файла в цикле, а затем перешел к просмотру другой страницы, метод stop останавливает циклическое проигрывание:

    if(fLoopPlay)
  auClip.stop();

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

    Назад


    Первое приложение Java

    Назад Вперед

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

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

    В качестве инструментального средства для разработки автономных приложений и аплетов Java мы будем использовать интегрированную систему разработки Java WorkShop. Она доступна для операционных систем Windows 95, Windows NT, Solaris (платформы SPARC и Intel).


    Установка Java WorkShop

    Назад Вперед

    Для установки Java WorkShop вы должны вставить дистрибутивный диск в устройство чтения CD-ROM. Программа установки запустится автоматически. Если этого не произошло (например, потому что автоматический запуск заблокирован), следует запустить программу установки вручную.

    Если вы работаете в среде Windows 95 или Windows NT, вам нужно запустить программу setup.exe из каталога Win32. На дистрибутивном диске Java WorkShop есть также установочные каталоги для других платформ.

    Процедура установки очень проста. Достаточно следовать инструкциям, которые появляются на экране. Единственное, что необходимо выбрать - это путь к каталогу, в который будут скопированы файлы Java WorkShop. При этом следует учесть, что для установки Java WorkShop в среде Windows 95 или Windows NT требуется примерно 50 Мбайт дисковой памяти.


    Запуск Java WorkShop

    Назад Вперед

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

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

    После запуска Java WorkShop на экране появится главное окно Java WorkShop (рис.1), а также окно Java WorkShop Startup (рис. 2).

    pic01sm.gif (2606 bytes) Рис. 1. Главное окно интегрированной системы разработки Java WorkShop

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

    pic02sm.gif (3257 bytes) Рис. 2. Окно Java WorkShop Startup

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

    Если раньше вы никогда не работали с Java WorkShop, окно Java WorkShop Startup поможет вам освоиться с основными процедурами. Нажав соответствующую кнопку, вы можете создать свой первый проект, открыть пример проекта, подготовленного для вас специалистами Sun Microsystems, ознакомиться с электронным руководством или справочной системой, а также открыть любой существующий проект.

    После приобретения небольшой практики можно включить переключатель Don't show me this window at startup, и окно Java WorkShop Startup перестанет появляться всякий раз, когда вы запускаете Java WorkShop.

    Создание нового проекта

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

    Всеми проектами в Java WorkShop управляет менеджер проектов. Чтобы открыть его окно, показанное на рис. 3, выберите строку Show Project Manager из меню Project главного окна Java WorkShop.

    pic03.gif (4211 bytes)

    Рис. 3. Окно Java WorkShop Project Manager

    Все проекты хранятся в портфелях (portfolios). В процессе установки автоматически создается ваш личный портфель personal, который находится в папке My Portfolios. В этом портфеле есть проекты Checkers, CardFile, JellyBeanExample и Performance Example. При создании нового проекта мы добавим его в портфель personal, хотя в дальнейшем вы сможете создавать новые портфели.

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

    Для создания проекта HelloJava выберите из меню File окна Java WorkShop Project Manager строку New, а затем в меню второго уровня строку Project. В результате будет запущен мастер создания проектов, первая диалоговая панель которого показана на рис. 4.

    pic04sm.gif (2051 bytes) Рис. 4. Первая диалоговая панель мастера проектов

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

    В поле Please, name this project введите название создаваемого проекта HelloJava, затем включите переключатели Standalone и No GUI. Первый из этих переключателей задает тип проекта (автономное приложение Java), второй указывает, что в проекте не используются средства автоматизированного проектирования пользовательского интерфейса.

    Далее нажмите кнопку Next. Вы окажитесь в диалоговой панели Create Project, показанной на рис. 5.

    pic05sm.gif (1881 bytes) Рис. 5. Диалоговая панель Create Project

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

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

    Теперь все готово к созданию проекта и вы можете нажать кнопку Finish. Мастер проектов создаст все необходимые файлы и сделает новый проект текущим. Основной файл проекта окажется загруженным в главное окно приложения Java WorkShop (рис. 6). Здесь вы можете его редактировать.

    pic06sm.gif (2479 bytes) Рис. 6. Основной файл созданного проекта загружен в окно приложения Java WorkShop

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

    Если теперь вы раскроете окно Java WorkShop Project Manager, то увидите, что в портфеле personal появился новый проект HelloJava. Раскрыв его, можно увидеть включенные в него файлы (рис. 7).

    pic07.gif (5130 bytes)

    Рис. 7. Файлы проекта HelloJava.

    Наш проект содержит только один файл с именем HelloJava.java. Отредактируйте этот файл, добавив в него следующую строку:

    System.out.println("Hello, Java!");

    Новый вариант исходного текста показан на рис. 8.

    pic08sm.gif (2810 bytes) Рис. 8. Измененный исходный текст приложения HelloJava

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

    Теперь выберите из меню Build главного окна Java WorkShop строку Build All. Через несколько мгновений исходные тексты приложения будут оттранслированы. Результат вы сможете увидеть нас странице Build блокнота, расположенного в нижней части окна Java WorkShop.

    Чтобы запустить приложение, выберите из меню Project строку Run. На экране появится консоль приложения с сообщением "Hello, Java!" (рис. 9).

    pic09.gif (3009 bytes)

    Рис. 9. Работа приложения Hello, Java!


    Исходный текст приложения HelloJava

    Назад Вперед

    Исходный текст нашего первого приложения состоит всего из нескольких строк:

    public class HelloJava
{
  public static void main(String args[])
  {
    System.out.println("Hello, Java!");
  }
}

    По своей простоте он не уступает известной программе "Hello, world!", с которой обычно начинают изучение языка программирования C.

    В нашем приложении определен один класс типа public с именем HelloJava. Заметим, что исходный файл приложения Java может содержать только один класс public, причем имя файла должно в точности совпадать с именем такого класса. В данном случае исходный файл называется HelloJava.java. Если бы вы назвали файл helloJava.java, компилятор выдал бы сообщение об ошибке.

    В классе HelloJava мастером проектов автоматически создается один статический метод с именем main.

    Если класс типа public с именем, совпадающем с именем файла, содержит определение метода main, то такой метод служит точкой входа автономного приложения Java. В этом он напоминает функцию main обычной программы, составленной на языке программирования C.

    В качестве параметра методу main передается ссылка на массив строк класса String. Через эти строки вы можете передавать приложению Java параметры запуска.

    Как наше приложение выводит текстовую строку на консоль?

    В классе System определена переменная класса PrintStream с именем out. В классе PrintStream определен метод println, при помощи которого приложение HelloJava выводит сообщение "Hello, Java!" на консоль.

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


Рис. 1. Главное окно интегрированной системы разработки Java WorkShop

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

pic01.gif (13937 bytes)

Рис. 1. Главное окно интегрированной системы разработки Java WorkShop


Рис. 2. Окно Java WorkShop Startup

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

pic02.gif (28888 bytes)

Рис. 2. Окно Java WorkShop Startup


Рис. 4. Первая диалоговая панель мастера проектов

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

pic04.gif (7604 bytes)

Рис. 4. Первая диалоговая панель мастера проектов


Рис.5. Диалоговая панель Create Project

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

pic05.gif (6541 bytes)

Рис.5. Диалоговая панель Create Project


Рис.6. Основной файл созданного проекта загружен в окно приложения Java WorkShop

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

pic06.gif (12070 bytes)

Рис.6. Основной файл созданного проекта загружен в окно приложения Java WorkShop


Рис. 8. Измененный исходный текст приложения HelloJava

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

pic08.gif (11184 bytes)

Рис. 8. Измененный исходный текст приложения HelloJava


Первый аплет Java

Первый аплет
Создание проекта
Текст аплета
Методы
Документ HTML
Новый текст
Метод paint
Метод getAppletInfo

    Первый аплет Java

    Назад Вперед

    В предыдущем разделе мы создавали автономное приложение Java, работающее под управлением виртуальной машины Java. Теперь мы создадим приложение другого типа - аплет.

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

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

    Двоичный файл с интерпретируемым байт-кодом Java располагается на сервере Web. В документе HTML с помощью оператора <APPLET> организуется ссылка на этот двоичный файл.

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

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


Создание проекта аплета
Текст аплета
Методы
Документ HTML
Новый текст
Метод paint
Метод getAppletInfo

    Создание проекта аплета

    Назад Вперед

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

    В первой диалоговой панели мастера проектов следует включить переключатели Applet и No GUI (рис. 1).

    pic01sm.gif (2059 bytes) Рис. 1. Первая диалоговая панель мастера проектов

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

    Сделав это, нажмите кнопку Next. На экране появится вторая диалоговая панель мастера проектов. Здесь вы должны указать путь к каталогу, куда мастер проектов запишет файлы проекта, а также включить переключатель No (рис. 2).

    pic02sm.gif (2059 bytes) Рис. 2. Вторая диалоговая панель мастера проектов

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

    В результате мастер проектов создаст исходные тексты аплета, а также добавит новый проект в портфель personal (если этот портфель остался активным в последний раз, когда вы запускали Java WorkShop). Новый проект называется HelloApplet (рис. 3).

    pic03.gif (5316 bytes)

    Рис. 3. Новый проект появился в активном портфеле personal

    Исходный текст аплета будет создан автоматически и загружен в окно редактирования системы Java WorkShop (рис. 4).

    pic01sm.gif (2059 bytes) Рис. 4. Исходный текст аплета загружен в окно редактирования

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

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

    Пока в окне нашего аплета ничего нет (рис. 5), однако скоро мы исправим это положение.

    pic05.gif (2199 bytes)

    Рис. 5. Окно аплета, созданного автоматически мастером проектов


Исходный текст аплета
Методы
Документ HTML
Новый текст
Метод paint
Метод getAppletInfo

    Исходный текст аплета

    Назад Вперед

    Полный исходный текст аплета, созданный автоматически мастером проектов Java WorkShop, мы представили в листинге 1.

    Листинг 1. Файл HelloApplet.java

    import java.applet.Applet;
public class HelloApplet extends Applet {
  /**
  * Initializes the applet.  You never need to
  * call this directly; it is
  * called automatically by the system once the
  * applet is created.
  */
  public void init() {}

  /**
  * Called to start the applet.  You never need
  * to call this directly; it
  * is called when the applet's
  * document is visited.
  */
  public void start() {}

  /**
  * Called to stop the applet.  This is called
  * when the applet's document is
  * no longer on the screen.  It is guaranteed
  * to be called before destroy()
  * is called.  You never need to
  * call this method directly
  */
  public void stop() {}

  /**
  * Cleans up whatever resources are being held.
  * If the applet is active
  * it is stopped.
  */
  public void destroy() {}
}

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

    import java.applet.Applet;
public class HelloApplet extends Applet
{
  public void init()    {}
  public void start()   {}
  public void stop()    {}
  public void destroy() {}
}

    Исходный текст нашего аплета начинается со строки, подключающей оператором import библиотеку классов java.applet.Applet.

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

    Напомним, что библиотека java.applet.Applet содержит классы, необходимые для создания аплетов, то есть разновидности приложений Java, встраиваемых в документы HTML и работающих под управлением браузера Internet.

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

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

    public class HelloApplet extends Applet
{
  . . .
}

    Определенный нами класс HelloApplet с помощью ключевого слова extends наследуется от класса Applet. При этом методам класса HelloApplet становятся доступными все методы и данные класса, за исключением определенных как private. Класс Applet определен в библиотеке классов java.applet.Applet, которую мы подключили оператором import.


Методы в классе HelloApplet
Документ HTML
Новый текст
Метод paint
Метод getAppletInfo

    Методы в классе HelloApplet

    Назад Вперед

    Создавая файл HelloApplet.java, мастер проектов системы Java WorkShop определила в классе HelloApplet несколько методов, заменив таким образом некоторые методы базового класса Applet.

    Метод init

    Метод init определен в базовом классе Applet, от которого наследуются все аплеты. Определение его таково, что этот метод ровным счетом ничего не делает.

    Когда вызывается метод init и зачем он нужен?

    Метод init вызывается тогда, когда браузер загружает в свое окно документ HTML с оператором <APPLET>, ссылающимся на данный аплет. В этот момент аплет может выполнять инициализацию, или, например, создавать потоки, если он работает в многопоточном режиме.

    Существует контрпара для метода init - метод destroy. О нем мы расскажем ниже.

    Метод destroy

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

    Методу destroy обычно поручают все необходимые операции, которые следует выполнить перед удалением аплета. Например, если в методе init вы создавали какие-либо потоки, в методе destroy их нужно завершить.

    Метод start

    Метод start вызывается после метода init в момент, когда пользователь начинает просматривать документ HTML с встроенным в него аплетом.

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

    Метод stop

    Дополнением к методу start служит метод stop. Он получает управление, когда пользователь покидает страницу с аплетом и загружает в окно браузера другую страницу. Заметим, что метод stop вызывается перед методом destroy.


Исходный текст документа HTML

Первый аплет
Создание проекта
Текст аплета
Методы
Исходный текст документа HTML Новый текст
Метод paint
Метод getAppletInfo

    Исходный текст документа HTML

    Назад Вперед

    Кроме файла исходного текста аплета мастер проектов создал файл документа HTML HelloApplet.tmp.html, представленный в листинге 2.

    Листинг 2. Файл HelloApplet.tmp.html

    <applet
   name="HelloApplet"
   code="HelloApplet"
   codebase="file:/e:/sun/vol3/src/HelloApplet"
   width="500"
   height="600"
   align="Top"
   alt="If you had a java-enabled
browser, you would see an applet here.">
</applet>

    С помощью оператора <applet> наш аплет встраивается в этот документ. Оператор <APPLET> используется в паре с оператором </APPLET> и имеет следующие параметры:

    Параметр | Описание
ALIGN | Выравнивание окна аплета относительно окружающего его текста. Возможны следующие значения: LEFT - выравнивание влево относительно окружающего текста; CENTER - центрирование; RIGHT - выравнивание вправо относительно окружающего текста; TOP - выравнивание по верхней границе; MIDDLE - центрирование по вертикали; BOTTOM - выравнивание по нижней границе
ALT | С помощью этого параметра можно задать текст, который будет отображаться в окне аплета в том случае, если браузер не может работать с аплетами Java
CODE | Имя двоичного файла, содержащего байт-код аплета. По умолчанию путь к этому файлу указывается относительно каталога с файлом HTML, в который встроен аплет. Такое поведение может быть изменено параметром CODEBASE
CODEBASE | Базовый адрес URL аплета, то есть путь к каталогу, содержащему аплет
HEIGHT | Ширина окна аплета в пикселах
WIDTH | Высота окна аплета в пикселах
HSPACE | Зазор слева и справа от окна аплета
VSPACE | Зазор сверху и снизу от окна аплета
NAME | Идентификатор аплета, который может быть использован другими аплетами, расположенными в одном и том же документе HTML, а также сценариями JavaScript
TITLE | Строка заголовка

    Дополнительно между операторами <APPLET> и </APPLET> вы можете задать параметры аплета. Для этого используется оператор <PARAM>, который мы рассмотрим позже.

    В нашем случае идентификатор NAME и имя двоичного файла заданы мастером проекта как "HelloApplet":

    name="HelloApplet"
code="HelloApplet"

    Параметр CODEBASE задает путь к каталогу локального диска:

    codebase="file:/e:/sun/vol3/src/HelloApplet"

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


Изменяем исходный текст аплета

Первый аплет
Создание проекта
Текст аплета
Методы
Документ HTML
Новый текст
Метод paint
Метод getAppletInfo

    Изменяем исходный текст аплета

    Назад Вперед

    Теперь давайте попробуем немного изменить исходный текст аплета, чтобы заставить его рисовать в своем окне текстовую строку "Hello, Java world".

    Вначале измените исходный текст так, как это показано на рис. 6.

    pic06sm.gif (2906 bytes) Рис. 6. Измененный исходный текст аплета HelloApplet

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

    Здесь мы намеренно внесли в исходный текст ошибку, чтобы показать, как Java WorkShop отреагирует на нее. Как видно из рисунка, сообщение об ошибке отображается на странице блокнота с названием Build. Текст сообщения гласит, что компилятор не смог найти определение класса Graphics, на который есть ссылка в девятой строке.

    Добавим строку импортирования класса java.awt.*, как это показано в листинге 3.

    Листинг 3. Файл HelloApplet.java (новый вариант)

    import java.applet.Applet;
import java.awt.*;

public class HelloApplet extends Applet
{
  public String getAppletInfo()
  {
    return "HelloJava Applet";
  }
  public void paint(Graphics g)
  {
    g.drawString("Hello, Java world!",
      20, 20);
  }
}

    Теперь исходный текст аплета транслируется без ошибок (рис. 7).

    pic07sm.gif (2925 bytes) Рис. 7. Измененный исходный текст успешно оттранслирован

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

    Если запустить аплет на выполнение, в его окне будет нарисована строка "Hello, Java world" (рис. 8).

    pic08.gif (2403 bytes)

    Рис. 8. Теперь наш аплет "умеет" рисовать в своем окне текстовые строки


Метод paint
Метод getAppletInfo

    Метод paint

    Назад Вперед

    Наиболее интересен для нас метод paint, который выполняет рисование в окне аплета. Вот его исходный текст:

    public void paint(Graphics g)
{
  g.drawString("Hello, Java world!",
  20, 20);
}

    Если посмотреть определение класса Applet, которое находится в файле JavaWorkshop20\JDK\src\java\applet\Applet.java, то в нем нет метода paint. В каком же классе определен этот метод?

    Иерархия классов

    Заглянем в документацию. Выберите из меню Help главного окна приложения Java WorkShop строку Java API Documentation. На экране появится окно браузера, встроенного в Java WorkShop. С помощью этого браузера вы сможете просматривать содержимое справочной системы.

    В разделе Java API Packages выберите библиотеку классов java.applet, а затем в разделе Class Index - строку Applet. Вы увидите иерархию классов:

    java.lang.Object
|
+---java.awt.Component
    |
    +---java.awt.Container
        |
        +---java.awt.Panel
            |
            +---java.applet.Applet

    Из этой иерархии видно, что класс java.applet.Applet произошел от класса java.awt.Panel. Этот класс, в свою очередь, определен в библиотеке классов java.awt и произошел от класса java.awt.Container.

    Продолжим наши исследования. В классе java.awt.Container снова нет метода paint, но сам этот класс создан на базе класса java.awt.Component.

    Но и здесь метода paint нет. Этот метод определен в классе java.awt.Component, который, в свою очередь, произошел от класса java.lang.Object и реализует интерфейс java.awt.image.ImageObserver.

    Таким образом мы проследили иерархию классов от класса java.applet.Applet, на базе которого создан наш аплет, до класса java.lang.Object, который является базовым для всех классов в Java.

    Метод paint определен в классе java.awt.Component, но так как этот класс является базовым для класса Applet и для нашего класса HelloApplet, мы можем переопределить метод paint.

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

    Метод paint вызывается, когда необходимо перерисовать окно аплета. Если вы создавали приложения для операционной системы Windows, то наверняка знакомы с сообщением WM_PAINT, которое поступает в функцию окна приложения при необходимости его перерисовки.

    Перерисовка окна приложения Windows и окна аплета обычно выполняется асинхронно по отношению к работе приложения или аплета. В любой момент времени аплет должен быть готов перерисовать содержимое своего окна.

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

    Обратите внимание, что методу paint в качестве параметра передается ссылка на объект Graphics:

    public void paint(Graphics g)
{
  . . .
}

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

    Наше приложение вызывает метод drawString, который рисует текстовую строку в окне аплета:

    g.drawString("Hello, Java world!", 20, 20);

    Вот прототип этого метода:

    public abstract void drawString(String str,
  int x, int y);

    Через первый параметр методу drawString передается текстовая строка в виде объекта класса String. Второй и третий параметр определяют, соответственно, координаты точки, в которой начнется рисование строки.

    В какой координатной системе?

    Аплеты используют систему координат, которая соответствует режиму отображения MM_TEXT, знакомому тем, кто создавал приложения Windows. Начало этой системы координат расположено в левом верхнем углу окна аплета, ось X направлена слева направо, а ось Y - сверху вниз (рис. 9).

    pic09.gif (1786 bytes)

    Рис. 9. Система координат, используемая методом drawString

    На этом же рисунке показано, как метод drawString нарисует текстовую строку с координатами (xCoord, yCoord).


Метод getAppletInfo

Первый аплет
Создание проекта
Текст аплета
Методы
Документ HTML
Новый текст
Метод paint
Метод getAppletInfo

    Метод getAppletInfo

    Назад Вперед

    Базовый класс Applet содержит определение метода getAppletInfo, возвращающее значение null. В нашем классе HelloApplet, который является дочерним по отношению к классу Applet, мы переопределили метод getAppletInfo из базового класса следующим образом:

    public String getAppletInfo()
{
  return "HelloJava Applet";
}

    Теперь метод getAppletInfo возвращает текстовую информацию об аплете в виде объекта класса String.

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


Рис. 1. Первая диалоговая панель мастера проектов

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

pic01.gif (7641 bytes)

Рис. 1. Первая диалоговая панель мастера проектов


Рис. 2. Вторая диалоговая панель мастера проектов

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

pic02.gif (6454 bytes)

Рис. 2. Вторая диалоговая панель мастера проектов


Рис. 4. Исходный текст аплета загружен в окно редактирования

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

pic04.gif (13006 bytes)

Рис. 4. Исходный текст аплета загружен в окно редактирования


Рис. 6. Измененный исходный текст аплета HelloApplet

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

pic06.gif (16818 bytes)

Рис. 6. Измененный исходный текст аплета HelloApplet


Рис. 7. Измененный исходный текст успешно оттранслирован

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

pic07.gif (16365 bytes)

Рис. 7. Измененный исходный текст успешно оттранслирован


Рисование в окне аплета

Рисование в окне
Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Рисование в окне аплета

    Назад Вперед

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

    Способ, которым аплет выполняет рисование в своем окне, полностью отличается от того, которым пользуются программы MS-DOS. Вместо того чтобы обращаться напрямую или через драйвер к регистрам видеоконтроллера, аплет пользуется методами из класса Graphics. Эти методы инкапсулируют все особенности аппаратуры, предоставляя в распоряжение программиста средство рисования, которое пригодно для любой компьютерной платформы.

    Для окна аплета создается объект класса Graphics, ссылка на который передается методу paint. Раньше мы уже пользовались этим объектом, вызывая для него метод drawString, рисующий в окне текстовую строку. Объект, ссылка на который передается методу paint, и есть контекст отображения. Сейчас мы займемся контекстом отображения вплотную.


Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Контекст отображения

    Назад Вперед

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

    Методы класса Graphics

    В качестве базового для класса Graphics (полное название класса java.awt.Graphics) выступает класс java.lang.Object.

    Прежде всего мы приведем прототипы конструктора этого класса и его методов с краткими комментариями. Полное описание вы сможете найти в электронной документации, которая входит в комплект Java WorkShop.

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

    Конструктор

    • Graphics
    protected Graphics();

    Методы

    • clearRect

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

    public abstract void clearRect(int x, int y,
  int width, int height);
    • clipRect

    Задание области ограничения вывода

    public abstract void clipRect(int x, int y,
  int width, int height);
    • copyArea

    Копирование содержимого прямоугольной области

    public abstract void copyArea(int x, int y,
  int width, int height, int dx, int dy);
    • create

    Создание контекста отображения

    public abstract Graphics create();

public Graphics create(int x, int y,
  int width, int height);
    • dispose

    Удаление контекста отображения

    public abstract void dispose();
    • draw3DRect

    Рисование прямоугольной области с трехмерным выделением

    public void draw3DRect(int x, int y,
  int width, int height, boolean raised);
    • drawArc

    Рисование сегмента

    public abstract void drawArc(int x, int y,
  int width, int height,
  int startAngle, int arcAngle);

    Рисование сегмента

    • drawBytes

    Рисование текста из массива байт

    public void drawBytes(byte data[],
  int offset, int length, int x, int y);
    • drawChars

    Рисование текста из массива символов

    public void drawChars(char  data[],
  int offset, int length, int x, int y);
    • drawImage

    Рисование растрового изображения

    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);
    • drawLine

    Рисование линии

    public abstract void drawLine(int x1, int y1,
  int x2, int y2);
    • drawOval

    Рисование овала

    public abstract void drawOval(int x, int y,
  int width, int height);
    • drawPolygon

    Рисование многоугольника

    public abstract void drawPolygon(
  int xPoints[],
  int yPoints[], int nPoints);

public void drawPolygon(Polygon p);
    • drawRect

    Рисование прямоугольника

    public void drawRect(int x, int y,
  int width, int height);
    • drawRoundRect

    Рисование прямоугольника с круглыми углами

    public abstract void drawRoundRect(
  int x, int y,
  int width, int height,
  int arcWidth, int arcHeight);
    • drawString

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

    public abstract void drawString(String str,
 int x, int y);
    • fill3DRect

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

    public void fill3DRect(int x, int y,
  int width, int height, boolean raised);
    • fillArc

    Рисование заполненного сегмента круга

    public abstract void fillArc(int x,
    int y, int width,
    int height, int startAngle,
    int arcAngle);
    • fillOval

    Рисование заполненного овала

    public abstract void fillOval(int x, int y,
  int width, int height);
    • fillPolygon

    Рисование заполненного многоугольника

    public abstract void fillPolygon(
  int xPoints[],
  int yPoints[], int nPoints);
    • fillPolygon

    Рисование заполненного многоугольника

    public void fillPolygon(Polygon p);

public abstract void fillRect(int x, int y,
  int width, int height);
    • fillRoundRect

    Рисование заполненного прямоугольника с круглыми углами

    public abstract void fillRoundRect(
  int x, int y,
  int width, int height,
  int arcWidth, int arcHeight);
    • finalize

    Прослеживание вызова метода dispose

    public void finalize();
    • getClipRect

    Определение границ области ограничения вывода

    public abstract Rectangle getClipRect();
    • getColor

    Определение цвета, выбранного в контекст отображения

    public abstract Color getColor();
    • getFont

    Определение шрифта, выбранного в контекст отображения

    public abstract Font getFont();
    • getFontMetrics

    Определение метрик текущего шрифта

    public FontMetrics getFontMetrics();
    • getFontMetrics

    Определение метрик заданного шрифта

    public abstract FontMetrics
  getFontMetrics(Font f);
    • setColor

    Установка цвета для рисования в контексте отображения

    public abstract void setColor(Color c);
    • setFont

    Установка текущего шрифта в контексте отображения

    public abstract void setFont(Font font);
    • setPaintMode

    Установка режима рисования

    Метод setPaintMode устанавливает в контексте отображения режим рисования, при котором выполняется замещение изображения текущим цветом, установленном в контексте отображения.

    public abstract void setPaintMode();
    • setXORMode

    Установка маски для рисования

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

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

    public abstract void setXORMode(Color c1);
    • translate

    Сдвиг начала системы координат

    Метод translate сдвигает начало системы координат в контексте отображения таким образом, что оно перемещается в точку с координатами (x, y), заданными через параметры метода:

    public abstract void translate(int x, int y);
    • toString

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

    public String toString();


Установка атрибутов контекста отображения

Рисование в окне
Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Установка атрибутов контекста отображения

    Назад Вперед

    Изменяя атрибуты контекста отображения, приложение 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("Hello, Java world!",
   10, 20);
  . . .
}

    Здесь мы привели фрагмент исходного текста метода paint, в котором в контексте отображения устанавливается желтый цвет. После этого метод drawString выведет текстовую строку " Hello, Java world!" желтым цветом.

    Если необходима более точная установка цвета, вы можете воспользоваться одним из трех конструкторов объекта 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.

    Класс Font

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

    Поля класса

    • name
    protected String name;
    • size
    protected int size;
    • style
    protected int style;

    Битовые маски стиля шрифта

    • BOLD
    public final static int BOLD;
    • ITALIC
    public final static int ITALIC;
    • PLAIN
    public final static int PLAIN;

    Конструкторы

    public Font(String name,
  int style, int size);

    Методы

    • equals

    Сравнение шрифтов

    public boolean equals(Object obj);
    • getFamily

    Определение названия семейства шрифтов

    public String getFamily();
    • getFont

    Получение шрифта по его характеристикам

    public static Font getFont(String  nm);
public static Font getFont(String  nm,
  Font  font);
    • getName

    Определение названия шрифта

    public String getName();
    • getSize

    Определение размера шрифта

    public int getSize();
    • getStyle

    Определение стиля шрифта

    public int getStyle();
    • hashCode

    Получение хэш-кода шрифта

    public int hashCode();
    • isBold

    Определение жирности шрифта

    public boolean isBold();
    • isItalic

    Проверка, является ли шрифт наклонным

    public boolean isItalic();
    • isPlain

    Проверка, есть ли шрифтовое выделение

    public boolean isPlain();
    • toString

    Получение текстовой строки для объекта

    public String toString();

    Создавая шрифт конструктором Font, вы должны указать имя, стиль и размер шрифта.

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

    Стиль шрифта задается масками BOLD, ITALIC и PLAIN, которые можно комбинировать при помощи логической операции "ИЛИ":

    Маска | Описание
BOLD | Утолщенный шрифт
ITALIC | Наклонный шрифт
PLAIN | Шрифтовое выделение не используется

    Что же касается размера шрифта, то он указывается в пикселах.


Определение атрибутов контекста отображения

Рисование в окне
Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Определение атрибутов контекста отображения

    Назад Вперед

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

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

    Определение границ области ограничения вывода

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

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

    public abstract Rectangle getClipRect();

    Метод возвращает ссылку на объект класса Rectangle, который, в частности, имеет поля класса с именами x, y, height и width. В этих полях находится, соответственно, координаты верхнего левого угла, высота и ширина прямоугольной области.

    Определение цвета, выбранного в контекст отображения

    Метод getColor возвращает ссылку на объект класса Color, представляющий текущий цвет, выбранный в контекст отображения:

    public abstract Color getColor();

    Определение шрифта, выбранного в контекст отображения

    С помощью метода getFont, возвращающего ссылку на объект класса Font, вы можете определить текущий шрифт, выбранный в контекст отображения:

    public abstract Font getFont();

    Определение метрик текущего шрифта

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

    Метрики текущего шрифта в контексте отображения вы можете узнать при помощи метода 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 без параметров возвращает метрики текущего шрифта, выбранного в контекст отображения.


Рисование геометрических фигур

Рисование в окне
Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Рисование геометрических фигур

    Назад Вперед

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

    Линии

    Для того чтобы нарисовать прямую тонкую сплошную линию, вы можете воспользоваться методом drawLine, прототип которого приведен ниже:

    public abstract void drawLine(int x1,
  int y1,int x2, int y2);

    Концы линии имеют координаты (x1, y1) и (x2, y2), как это показано на рис. 1.

    pic01.gif (2176 bytes)

    Рис. 1. Рисование прямой линии

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

    Прямоугольники и квадраты

    Среди методов класса Graphics есть несколько, предназначенных для рисования прямоугольников. Первый из них, с именем drawRect, позволяет нарисовать прямоугольник, заданный координатами своего левого верхнего угла, шириной и высотой:

    public void drawRect(int x, int y,
  int width, int height);

    Параметры x и y задают, соответственно, координаты верхнего левого угла, а параметры width и height - высоту и ширину прямоугольника (рис. 2).

    pic02.gif (2472 bytes)

    Рис. 2. Рисование прямоугольника

    В отличие от метода drawRect, рисующего только прямоугольную рамку, метод fillRect рисует заполненный прямоугольник. Для рисования и заполнения прямоугольника используется цвет, выбранный в контекст отображения (рис. 3).

    pic03.gif (3126 bytes)

    Рис. 3. Рисование заполненного прямоугольника

    Прототип метода fillRect приведен ниже:

    public abstract void fillRect(int x, int y,
  int width, int height);

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

    public abstract void drawRoundRect(int x,
  int y, int width,
  int height, int arcWidth, int arcHeight);

    Параметры x и y определяют координаты верхнего левого угла прямоугольника, параметры width и height задают, соответственно его ширину и высоту.

    Размеры эллипса, образующего закругления по углам, вы можете задать с помощью параметров arcWidth и arcHeight. Первый из них задает ширину эллипса, а второй - высоту (рис. 4).

    pic04.gif (3197 bytes)

    Рис. 4. Рисование прямоугольника с закругленными углами

    Метод fillRoundRect позволяет нарисовать заполненный прямоугольник с закругленными углами (рис. 5).

    pic05.gif (3887 bytes)

    Рис. 5. Рисование заполненного прямоугольника с закругленными углами

    Назначение параметров этого метода аналогично назначению параметров только что рассмотренного метода drawRoundRect:

    public abstract void fillRoundRect(int x,
 int y,
 int width, int height,
 int arcWidth, int arcHeight);

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

    public void fill3DRect(int x, int y,
  int width, int height, boolean raised);

    Если значение параметра raised равно true, рисуется выступающий прямоугольник, если false - западающий. Назначение остальных параметров аналогично назначению параметров метода drawRect.

    Многоугольники

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

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

    public abstract void drawPolygon(
  int xPoints[],
  int yPoints[], int nPoints);

    Через параметры xPoints и yPoints передаются, соответственно, ссылки на массивы координат по оис X и Y. Параметр nPoints задает количество точек в массивах.

    На рис. 6 показан многоугольник, нарисованный методом drawPolygon.

    pic06.gif (2719 bytes)

    Рис. 6. Многоугольник, нарисованный методом drawPolygon

    В этом многоугольнике шесть вершин с координатами от (x0, y0) до (x5, y5), причем для того чтобы он стал замкнутым, координаты первой и последней вершины совпадают.

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

    public void drawPolygon(Polygon p);

    Класс Polygon достаточно прост. Приведем описание его полей, конструкторов и методов:

    Поля класса

    • npoints

    Количество вершин

    public int npoints;
    • xpoints

    Массив координат по оси X

    public int xpoints[];
    • ypoints

    Массив координат по оси Y

    public int ypoints[]

    Конструкторы

    public Polygon ();
public Polygon(int xpoints[],
  int ypoints[], int npoints);

    Методы

    • addPoint

    Добавление вершины

    public void addPoint(int x, int y);
    • getBoundingBox

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

    public Rectangle getBoundingBox();
    • inside

    Проверка, находится ли точка внутри многоугольника

    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);

    Если вам нужно нарисовать заполненный многоугольник (рис. 7), то для этого вы можете воспользоваться методами, приведенными ниже:

    public abstract void fillPolygon(
 int xPoints[],
 int yPoints[], int nPoints);

public void fillPolygon(Polygon p);

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

    pic07.gif (3128 bytes)

    Рис. 7. Многоугольник, нарисованный методом fillPolygon

    Овалы и круги

    Для рисования окружностей и овалов вы можете воспользоваться методом drawOval:

    public abstract void drawOval(
  int x, int y,
  int width, int height);

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

    pic08.gif (2591 bytes)

    Рис. 8. Рисование овала

    Метод fillOval предназначен для рисования заполненного овала (рис. 9). Назначение его параметров аналогично назначению параметров метода drawOval:

    public abstract void fillOval(
  int x, int y,
  int width, int height);

    pic09.gif (3122 bytes)

    Рис. 9. Рисование заполненного овала

    Сегменты

    Метод drawArc предназначен для рисования незаполненного сегмента (рис. 10). Прототип этого метода приведен ниже:

    public abstract void drawArc(
 int x, int y,
 int width, int height,
 int startAngle, int arcAngle);

    pic10.gif (3409 bytes)

    Рис. 10. Рисование незаполненного сегмента

    Параметры x, y, width и height задают координаты прямоугольника, в который вписан сегмент.

    Параметры startAngle и arcAngle задаются в градусах. Они определяют, соответственно, начальный угол и угол разворота сегмента.

    Для того чтобы нарисовать заполненный сегмент, вы можете воспользоваться методом fillArc:

    public abstract void fillArc(int x, int y,
  int width, int height,
  int startAngle, int arcAngle);

    Задание области ограничения

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

    public abstract void clipRect(
 int x, int y,
 int width, int height);

    Параметры x, y, width и height задают координаты прямоугольной области ограничения.

    Копирование содержимого прямоугольной области

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

    public abstract void copyArea(
 int x, int y,
 int width, int height, int dx, int dy);

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


Аплет Draw
Исходные тексты
Проект аплета

    Аплет Draw

    Назад Вперед

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

    На рис. 11 показано окно этого аплета.

    pic11.gif (4305 bytes)

    Рис. 11. Окно аплета Draw

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

    В нижней части окна нарисовано несколько геометрических фигур.


Исходные тексты аплета Draw

Рисование в окне
Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Исходные тексты аплета Draw

    Назад Вперед

    Исходные тексты аплета Draw вы найдете в листинге 1.

    Листинг 1. Файл draw.java

    import java.applet.*;
import java.awt.*;

public class draw extends Applet
{
  Toolkit tk;
  String szFontList[];
  FontMetrics fm;
  int yStart = 20;
  int yStep;
  String parm_TestString;

  public void init()
  {
    tk = Toolkit.getDefaultToolkit();
    szFontList = tk.getFontList();
    parm_TestString =
      getParameter("TestString");
  }

  public String getAppletInfo()
  {
    return "Name: draw";
  }

  public void paint(Graphics g)
  {
    int yDraw;
    Dimension dimAppWndDimension = getSize();

    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);

    fm = g.getFontMetrics();
    yStep = fm.getHeight();

    for(int i = 0; i < szFontList.length; i++)
    {
      g.setFont(new Font("Helvetica",
        Font.PLAIN, 12));
      g.drawString(szFontList[i], 10,
        yStart + yStep * i);

      fm = g.getFontMetrics();
      yStep = fm.getHeight();

      g.setFont(new Font(szFontList[i],
        Font.PLAIN, 12));
      g.drawString(parm_TestString,
        100, yStart + yStep * i);
    }

    yDraw = yStart + yStep * szFontList.length
      + yStep;

    Polygon p = new Polygon();

    p.addPoint(70, yDraw);
    p.addPoint(150, yDraw + 30);
    p.addPoint(160, yDraw + 80);
    p.addPoint(190, yDraw + 60);
    p.addPoint(140, yDraw + 30);
    p.addPoint(70, yDraw + 39);

    g.drawPolygon(p);

    g.setColor(Color.red);
    g.drawRect(10, yDraw + 85, 200, 100);

    g.setColor(Color.black);
    g.drawArc(10, yDraw + 85,
      200, 100, -50, 320);
  }

  public String[][] getParameterInfo()
  {
    String[][] info =
    {
      {
    "TestString", "String", "Test string"
      }
    };
    return info;
  }
}

    Метод init

    При инициализации аплета метод init извлекает список доступных шрифтов и принимает значение параметра TestString, передаваемое аплету в документе HTML.

    Извлечение списка шрифтов

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

    Toolkit tk;
String szFontList[];
  . . .
tk = Toolkit.getDefaultToolkit();
szFontList = tk.getFontList();

    Аплет вызывает статический метод getDefaultToolkit из класса Toolkit и затем, пользуясь полученной ссылкой, извлекает список шрифтов, записывая его в массив szFontList.

    Для чего еще можно использовать класс Toolkit?

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

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

    • getDefaultToolkit

    Получение ссылки на Toolkit

    public static Toolkit getDefaultToolkit();
    • getColorModel

    Определение текущей цветовой модели, выбранной в контекст отображения

    public abstract ColorModel getColorModel();
    • getFontList

    Получение списка шрифтов, доступных аплету

    public abstract String[] getFontList();
    • getFontMetrics

    Получение метрик заданного шрифта

    public abstract FontMetrics
  getFontMetrics(Font font);
    • getImage

    Получение растрового изображения по имени файла

    public abstract Image
  getImage(String filename);
    • getImage

    Получение растрового изображения по адресу URL

    public abstract Image getImage(URL  url);
    • getScreenResolution

    Определение разрешения экрана в точках на дюйм

    public abstract int getScreenResolution();
    • getScreenSize

    Размеры экрана в пикселах

    public abstract Dimension getScreenSize();
    • prepareImage

    Подготовка растрового изображения для вывода

    public abstract boolean prepareImage(
  Image image,
  int width, int height,
  ImageObserver observer);
    • sync

    Синхронизация состояния Toolkit

    public abstract void sync();

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

    Получение значения параметров

    До сих пор наши аплеты не получали параметров из документов HTML, в которые мы их встраивали.

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

    Пользуясь операторами <PARAM>, расположенными в документе HTML сразу после оператора <APPLET>, можно передать аплету произвольное количество параметров, например, в виде текстовых строк:

    <applet
    code=MyApplet.class
    id=MyApplet
     . . .
    width=320
    height=240 >
    <param name=ParamName1 value="Value 1">
    <param name=ParamName2 value="Value 2">
    <param name=ParamName3 value="Value 3">
    <param name=ParamName4 value="Value 4">
     . . .
</applet>

    Здесь через параметр NAME оператора <PARAM> передается имя параметра аплета, а через параметр VALUE - значение соответствующего параметра.

    Как параметр может получить значение параметров?

    Для получения значения любого параметра аплет должен использовать метод getParameter. В качестве единственного параметра этому методу передается имя параметра аплета в виде строки типа String, например:

    parm_TestString = getParameter("TestString");

    Обычно в аплете также определяется метод getParameterInfo, возвращающий информацию о параметрах. Вот исходный текст этого метода для нашего аплета Draw:

    public String[][] getParameterInfo()
{
  String[][] info =
  {
    {
      "TestString", "String", "Test string"
    }
  };
  return info;
}

    Метод getParameterInfo возвращает массив массивов текстовых строк. Первая строка указывает имя параметра, вторая - тип передаваемых через него данных, а третья - значение параметра по умолчанию.

    Метод paint

    Первым делом метод paint определяет размеры окна аплета, вызывая для этого метод getSize:

    Dimension dimAppWndDimension = getSize();

    Метод getSize возвращает ссылку на объект класса Dimension, хранящий высоту и ширину объекта:

    • height

    Высота

    public int height;
    • width

    Ширина

    public int width;

    В классе Dimension предусмотрено три конструктора и один метод:

    public Dimension();
public Dimension(Dimension  d);
public Dimension(int width, int height);
    • toString

    Получение строки, представляющей класс

    public String toString();

    После определения размеров окна аплета метод paint стирает содержимое всего окна:

    g.clearRect(0, 0,
  dimAppWndDimension.width  - 1,
  dimAppWndDimension.height - 1);

    Далее в контексте отображения устанавливается желтый цвет:

    g.setColor(Color.yellow);

    Этим цветом заполняется внутренняя область окна аплета, для чего применяется метод fillRect:

    g.fillRect(0, 0,
  dimAppWndDimension.width  - 1,
  dimAppWndDimension.height - 1);

    Затем метод paint устанавливает в контексте отображения черный цвет и рисует тонкую черную рамку вокруг окна аплета:

    g.setColor(Color.black);
g.drawRect(0, 0,
  dimAppWndDimension.width  - 1,
  dimAppWndDimension.height - 1);

    На следующем этапе мы получаем метрики текущего шрифта, выбранного в контекст отображения:

    fm = g.getFontMetrics();

    Пользуясь этими метриками, мы определяем высоту символов текущего шрифта и записываем ее в поле yStep:

    yStep = fm.getHeight();

    После этого метод paint запускает цикл по всем шрифтам, установленным в системе:

    for(int i = 0; i < szFontList.length; i++)
{
  . . .
}

    Количество доступных шрифтов равно размеру массива szFontList, которое вычисляется как szFontList.length.

    Метод paint выписывает в окне аплета название каждого шрифта, устанавливая для этого шрифт Helvetica размером 12 пикселов:

    g.setFont(new Font("Helvetica",
  Font.PLAIN, 12));
g.drawString(szFontList[i],
  10, yStart + yStep * i);

    Смещение каждой новой строки с названием шрифта вычисляется исходя из высоты символов установленного шрифта:

    fm = g.getFontMetrics();
yStep = fm.getHeight();

    После названия шрифта метод paint рисует в окне аплета текстовую строку parm_TestString, полученную через параметр с именем "TestString":

    g.setFont(new Font(szFontList[i],
  Font.PLAIN, 12));
g.drawString(parm_TestString,
  100, yStart + yStep * i);

    Перед тем как перейти к рисованию геометрических фигур, метод paint запоминает в поле yDraw координату последней строки названия шрифта, сделав отступ высотой yStep :

    int yDraw;
yDraw = yStart +
  yStep * szFontList.length + yStep;

    Первая фигура, которую рисует наш аплет, это многоугольник.

    Мы создаем многоугольник как объект класса Polygon:

    Polygon p = new Polygon();

    В этот объект при помощи метода addPoint добавляется несколько точек:

    p.addPoint(70,  yDraw);
p.addPoint(150, yDraw + 30);
p.addPoint(160, yDraw + 80);
p.addPoint(190, yDraw + 60);
p.addPoint(140, yDraw + 30);
p.addPoint(70,  yDraw + 39);

    После добавления всех точек метод paint рисует многоугольник, вызывая для этого метод drawPolygon:

    g.drawPolygon(p);

    Затем мы устанавливаем в контексте отображения красный цвет и рисуем прямоугольник:

    g.setColor(Color.red);
g.drawRect(10, yDraw + 85, 200, 100);

    Затем метод paint вписывает в этот прямоугольник сегмент окружности:

    g.setColor(Color.black);
g.drawArc(10, yDraw + 85,
  200, 100, -50, 320);

    Документ HTML для аплета Draw

    Документ HTML для аплета Draw не имеет никаких особенностей. Он представлен в листинге 2.

    Листинг 2. Файл draw.tmp.html

    <applet
   name="draw"
   code="draw"
   codebase="file:/e:/Sun/Articles/vol4/src/draw"
   width="250"
   height="350"
   align="Top"
   alt="If you had a java-enabled browser,
 you would see an applet here."
>
<param name="TestString" value="Test string">
   <hr>If your browser recognized the applet tag,
   you would see an applet here.<hr>
</applet>


Проект для аплета Draw

Рисование в окне
Контекст отображения
Установка атрибутов
Определение атрибутов
Рисование фигур
Аплет Draw
Исходные тексты
Проект аплета

    Проект для аплета Draw

    Назад Вперед

    Подготовьте файлы проекта аплета Draw, скопировав их из предыдущего раздела в какой-нибудь каталог. Затем запустите мастер проектов и в соответствующей диалоговой панели укажите путь к этому каталогу (рис. 12).

    pic12sm.gif (1779 bytes) Рис. 12. Указание пути к каталогу с исходными файлами

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

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

    После этого вы увидите на экране диалоговую панель, показанную на рис. 13.

    pic13sm.gif (1801 bytes) Рис. 13. Добавление к проекту существующих файлов

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

    Здесь вам нужно нажать кнопку Add All in Directory. Как только вы это сделаете, в списке файлов Project Files появится имя файла draw.java, подготовленного вами заранее.

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

    pic14sm.gif (1610 bytes) Рис. 14. Задание имени главного класса

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

    Нажав кнопку Finish, вы можете завершить формирование проекта.

    Hаш аплет принимает из документа HTML один параметр с именем TestString. Для добавления параметров вам нужно открыть окно менеджера проектов, выбрав из меню Project строку Show Project Manager. В этом окне проект draw должен быть текущим.

    Выделите его курсором мыши в окне Java WorkShop Project Manager и из меню Project этого окна выберите строку Edit. Вы увидите блокнот Project Edit, с помощью которого можно изменять различные параметры проекта. Откройте в этом блокноте страницу Run (рис. 15).

    pic15sm.gif (2214 bytes) Рис. 15. Добавление параметра TestString

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

    В полях Name и Value введите, соответственно, имя параметра и значение параметра по умолчанию, а затем нажмите кнопку Add. Добавленный вами параметр появится в списке Parameters. Теперь вам осталось только нажать кнопку OK.


Рис. 12. Указание пути к каталогу с исходными файлами

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

pic12.gif (6658 bytes)

Рис. 12. Указание пути к каталогу с исходными файлами


Рис. 13. Добавление к проекту существующих файлов

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

pic13.gif (7498 bytes)

Рис. 13. Добавление к проекту существующих файлов


Рис. 14. Задание имени главного класса

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

pic14.gif (4980 bytes)

Рис. 14. Задание имени главного класса


Рис. 15. Добавление параметра TestString

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

pic15.gif (12025 bytes)

Рис. 15. Добавление параметра TestString


События
Обработка событий
События от мыши
Аплет LineDraw
Исходный текст
Описание текста

    События

    Назад Вперед

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

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

    Если вы создавали приложения для операционной системы Microsoft Windows, здесь для вас нет ничего нового - вспомните, как вы обрабатывали сообщение WM_LBUTTONDOWN или WM_CHAR. Когда пользователь выполнял действие с мышью или клавиатурой в окне приложения, функция этого окна получала соответствующее сообщение. Методы класса Applet, обрабатывающие события от мыши и клавиатуры, являются аналогами обработчиков указанных сообщений.

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


    Как обрабатываются события

    Назад Вперед

    Когда возникает событие, управление получает метод handleEvent из класса Component. Класс Applet является дочерним по отношению к классу Component.

    Прототип метода handleEvent мы привели ниже:

    public boolean handleEvent(Event evt);

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

    Ниже мы привели список полей класса Event, которые вы можете проанализировать:

    Поле | Описание
public Object argarg; | Произвольный аргумент события, значение которого зависит от типа события
public int clickCountclickCount; | Это поле имеет значение только для события с типом MOUSE_DOWNMOUSE_DOWN и содержит количество нажатий на клавишу мыши. Если пользователь сделал двойной щелчок мышью, в это поле будет записано значение 2
public Event evtevt; | Следующее событие в связанном списке
public int idid; | Тип события. Ниже мы перечислим возможные значения для этого поля
public int keykey; | Код нажатой клавиши (только для события, созданного при выполнении пользователем операции с клавиатурой)
public int modifiersmodifiers; | Состояние клавиш модификации <Alt>, <Ctrl>, <Shift>
public Object targettarget; | Компонент, в котором произошло событие
public long whenwhen; | Время, когда произошло событие
public int xx; | Координата по оси X
public int yy; | Координата по оси Y

    Поле id (тип события) может содержать следующие значения:

    Значение | Тип события
ACTION_EVENTACTION_EVENT | Пользователь хочет, чтобы произошло некоторое событие
GOT_FOCUSGOT_FOCUS | Компонент (в нашем случае окно аплета) получил фокус ввода. О фокусе ввода вы узнаете из раздела, посвященного работе с клавиатурой
KEY_ACTIONKEY_ACTION | Пользователь нажал клавишу типа "Action"
KEY_ACTION_RELEASEKEY_ACTION_RELEASE | Пользователь отпустил клавишу типа "Action"
KEY_PRESSKEY_PRESS | Пользователь нажал обычную клавишу
KEY_RELEASEKEY_RELEASE | Пользователь отпустил обычную клавишу
LIST_DESELECTLIST_DESELECT | Отмена выделения элемента в списке
LIST_SELECTLIST_SELECT | Выделение элемента в списке
LOAD_FILELOAD_FILE | Загрузка файла
LOST_FOCUSLOST_FOCUS | Компонент потерял фокус ввода
MOUSE_DOWNMOUSE_DOWN | Пользователь нажал клавишу мыши
MOUSE_DRAGMOUSE_DRAG | Пользователь нажал клавишу мыши и начал выполнять перемещение курсора мыши
MOUSE_ENTERMOUSE_ENTER | Курсор мыши вошел в область окна аплета
MOUSE_EXITMOUSE_EXIT | Курсор мыши покинул область окна аплета
MOUSE_MOVEMOUSE_MOVE | Пользователь начал выполнять перемещение курсора мыши, не нажимая клавишу мыши
MOUSE_UPMOUSE_UP | Пользователь отпустил клавишу мыши
SAVE_FILESAVE_FILE | Сохранение файла
SCROLL_ABSOLUTESCROLL_ABSOLUTE | Пользователь переместил движок полосы просмотра в новую позицию
SCROLL_LINE_DOWNSCROLL_LINE_DOWN | Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вниз
SCROLL_LINE_UPSCROLL_LINE_UP | Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вверх
SCROLL_PAGE_DOWNSCROLL_PAGE_DOWN | Пользователь выполнил над полосой просмотра операцию сдвига на одну страницу вниз
SCROLL_PAGE_UPSCROLL_PAGE_UP | Пользователь выполнил над полосой просмотра операцию сдвига на одну страницувверх
WINDOW_DEICONIFYWINDOW_DEICONIFY | Пользователь запросил операцию восстановления нормального размера окна после его минимизации
WINDOW_DESTROYWINDOW_DESTROY | Пользователь собирается удалить окно
WINDOW_EXPOSEWINDOW_EXPOSE | Окно будет отображено
WINDOW_ICONIFYWINDOW_ICONIFY | Окно будет минимизировано
WINDOW_MOVEDWINDOW_MOVED | Окно будет перемещено

    Если событие связано с клавиатурой (тип события KEY_ACTION или KEY_ACTION_RELEASE), в поле key может находиться одно из следующих значений:

    Значение | Клавиша
DOWNDOWN | Клавиша перемещения курсора вниз
ENDEND | <End>
F1F1-F12 | <F1>
F2 | <F2>
F3 | <F3>
F4 | <F4>
F5 | <F5>
F6 | <F6>
F7 | <F7>
F8 | <F8>
F9 | <F9>
F10 | <F10>
F11 | <F11>
F12 | <F12>
HOMEHOME | <Home>
LEFTLEFT | Клавиша перемещения курсора влево
PGDNPGDN | <Page Down>
PGUPPGUP | <Page Up>
RIGHTRIGHT | Клавиша перемещения курсора вправо
UPUP | Клавиша перемещения курсора вниз

    Могут быть указаны следующие маски для поля модификаторов modifiers:

    Значение маски | Описание
ALT_MASKALT_MASK | Была нажата клавиша <Alt>
META_MASKMETA_MASK | Была нажата мета-клавиша (клавиша для ввода диактрических символов)
CTRL_MASKCTRL_MASK | Была нажата клавиша <Ctrl>
SHIFT_MASKSHIFT_MASK | Была нажата клавиша <Shift>

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


События от мыши
Аплет LineDraw
Исходный текст
Описание текста

    События от мыши

    Назад Вперед

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

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

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

    Нажатие клавиши мыши

    Переопределив метод 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");

    Отпускание клавиши мыши

    При отпускании клавиши мыши управление получает метод mouseUp:

    public boolean mouseUp(Event evt, int x, int y);

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

    Перемещение курсора мыши

    Когда пользователь перемещает курсор мыши над окном аплета, в процессе перемещения происходит вызов метода mouseMove:

    public boolean mouseMove(Event evt, int x, int y);

    Через переменные x и y передаются текущие координаты курсора мыши.

    Выполнение операции Drag and Drop

    Операция Drag and Drop выполняется следующим образом: пользователь нажимает клавишу мыши и, не отпуская ее, начинает перемещать курсор мыши. При этом происходит вызов метода mouseDrag:

    public boolean mouseDrag(Event evt, int x, int y);

    Через переменные x и y передаются текущие координаты курсора мыши. Метод mouseDrag вызывается даже в том случае, если в процессе перемещения курсор вышел за пределы окна аплета.

    Вход курсора мыши в область окна аплета

    Метод mouseEnter получает управление, когда курсор мыши в процессе перемещения по экрану попадает в область окна аплета:

    public boolean mouseEnter(Event evt, int x, int y);

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

    Выход курсора мыши из области окна аплета

    Метод mouseExit вызывается при покидании куросром окна аплета:

    public boolean mouseExit(Event evt, int x, int y);

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


Аплет LineDraw
Исходный текст
Описание текста

    Аплет LineDraw

    Назад Вперед

    В аплете LineDraw мы показали основные приемы обработки событий, вызываемых мышью. Вы можете рисовать в окне аплета прямые линии, причем возле концов линий отображаются их координаты (рис. 1).

    pic01.gif (4730 bytes)

    Рис. 1. Окно аплета LineDraw с нарисованными в нем линиями

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

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

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


    Исходный текст аплета LineDraw

    Назад Вперед

    Исходный текст аплета LieDrnaw вы найдете в листинге 1.

    Листинг 1. Файл LieDrnaw.java

    import java.applet.*;
import java.awt.*;
import java.util.*;

public class LineDraw extends Applet
{
  Dimension dmDown;
  Dimension dmUp;
  Dimension dmPrev;
  boolean bDrawing;
  Vector lines;
      public String getAppletInfo()
  {
    return "Name: LineDraw";
  }
      public void init()
  {
    bDrawing = false;
    lines = new Vector();
  }
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension = getSize();
        setBackground(Color.yellow);
    g.setColor(Color.black);
    g.drawRect(0, 0,
      dimAppWndDimension.width  - 1,
      dimAppWndDimension.height - 1);
        for (int i=0; i < lines.size(); i++)
    {
      Rectangle p =
        (Rectangle)lines.elementAt(i);
      g.drawLine(p.width, p.height,
        p.x, p.y);
      g.drawString("<" + p.width
        + "," + p.height + ">",
        p.width, p.height);
      g.drawString("<" + p.x + "," +
        p.y+ ">", p.x, p.y);
    }
    bDrawing = false;
  }
      public boolean mouseDown(Event evt,
    int x, int y)
  {
    if(evt.clickCount > 1)
    {
      lines.removeAllElements();
      repaint();
      return true;
    }
        dmDown = new Dimension(x, y);
    dmPrev = new Dimension(x, y);
    bDrawing = false;
    return true;
  }
      public boolean mouseUp(Event evt,
    int x, int y)
  {
    if(bDrawing)
    {
      dmUp = new Dimension(x, y);
      lines.addElement(
        new Rectangle(dmDown.width,
        dmDown.height, x, y));
      repaint();
      bDrawing = false;
    }
    return true;
  }
      public boolean mouseDrag(Event evt,
    int x, int y)
  {
    Graphics g = getGraphics();
    bDrawing = true;
        g.setColor(Color.yellow);
    g.drawLine(dmDown.width, dmDown.height,
      dmPrev.width, dmPrev.height);
        g.setColor(Color.black);
    g.drawLine(dmDown.width,
      dmDown.height, x, y);
        dmPrev = new Dimension(x, y);
    return true;
  }
      public boolean mouseMove(Event evt,
    int x, int y)
  {
    bDrawing = false;
    return true;
  }
}

    Исходный текст документа HTML, подготовленного системой JavaWorkshop, представлен в листинге 2.

    Листинг 2. Файл LineDraw.tmp.html

    <applet name="LineDraw"
  code="LineDraw"
  codebase=
"file:/e:/Sun/Articles/vol5/src/LineDraw"
  width="500"
  height="600"
  align="Top"
  alt="If you had a java-enabled browser,
you would see an applet here.">
</applet>


    Описание исходного текста

    Назад Вперед

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

    Класс Vector имеет полное имя java.util.Vector, поэтому мы подключаем соответствующую библиотеку классов:

    import java.util.*;

    Поля класса LineDraw

    В нашем классе мы определили несколько полей, предназначенных для хранения текущих координат рисуемых линий:

    Dimension dmDown;
Dimension dmUp;
Dimension dmPrev;
boolean   bDrawing;
Vector    lines;

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

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

    В процессе рисования линии метод mouseDrag стирает ранее нарисованную линию и рисует новую. Координаты конца старой линии хранятся в переменной dmPrev.

    Переменная bDrawing типа boolean хранит текущее состояние аплета. Когда аплет находится в состоянии рисования линии, в эту переменную записывается значение true, а когда нет - значение false.

    И, наконец, переменная lines типа Vector является динамическим массивом, в котором хранятся координаты нарисованных линий.

    Метод getAppletInfo

    Метод getAppletInfo возвращает название аплета и не имеет никаких особенностей.

    Метод init

    Метод init сбрасывает признак рисования, записывая в поле bDrawing значение false, а также создает новый динамический массив в виде объекта класса Vector:

    public void init()
{
  bDrawing = false;
  lines = new Vector();
}

    Метод paint

    После изменения цвета фона и рисования рамки метод 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);

  g.drawString("<" + p.width + ","
    + p.height + ">",
    p.width, p.height);

  g.drawString("<" + p.x + "," +
    p.y+ ">", p.x, p.y);
}

    Для объектов класса Vector метод size возвращает количество элементов в массиве, чем мы воспользовались для проверки условия выхода из цикла.

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

    Так как в массиве хранятся объекты класса Rectangle, перед инициализацией ссылки p мы выполняем явное преобразование типов.

    Координаты концов линий рисуются с помощью уже знакомого вам метода drawString.

    Перед завершением работы метод paint сбрасывает признак рисования, записывая в поле bDrawing значение false:

    bDrawing = false;

    Метод mouseDown

    В начале своей работы метод mouseDown определяет, был ли сделан одинарный щелчок клавишей мыши, или двойной. Если был сделан двойной щелчок мышью, метод удаляет все элементы из массива list, а затем перерисовывает окно аплета, вызывая метод repaint:

    lines.removeAllElements();
repaint();

    После перерисовки окно аплета очищается от линий.

    Если же был сделан одинарный щелчок клавишей мыши, метод mouseDown сохраняет текущие координаты курсора в переменных dmDown и dmPrev, а затем сбрасывает признак рисования:

    dmDown = new Dimension(x, y);
dmPrev = new Dimension(x, y);
bDrawing = false;

    Метод mouseUp

    Когда пользователь отпускает клавишу мыши, вызывается метод mouseUp. В его задачу входит сохранение текущих координат курсора мыши в поле dmUp, а также добавление нового элемента в массив lines:

    dmUp = new Dimension(x, y);
lines.addElement(
  new Rectangle(dmDown.width,
  dmDown.height, x, y));
repaint();

    После добавления элемента в массив метод mouseUp инициирует перерисовку окна аплета, вызывая для этого метод repaint.

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

    Метод 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 не делает ничего, за исключением того, что он отключает режим рисования. Таким образом, простое перемещение курсора мыши над окном аплета не приводит к рисованию линий.


Компоненты в окне аплета

Компоненты
Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Компоненты в окне аплета

    Назад Вперед

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

    В окне аплета вы также можете разместить некоторые из перечисленных выше органов управления, а именно:

    • кнопки;
    • переключатели с независимой фиксацией;
    • переключатели с зависимой фиксацией;
    • статические текстовые поля;
    • однострочные и многострочные поля редактирования текста;
    • списки;
    • полосы просмотра

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

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

    pic01.gif (3954 bytes)

    Рис. 1. Взаимосвязь классов органов управления в приложениях Java

    Класс Button позволяет создавать стандартные кнопки. Если вам нужна нестандартная кнопка (например, графическая кнопка), вы можете создать ее на базе класса Canvas.

    Для создания переключателей с независимой или зависимой фиксацией предназначен класс CheckBox.

    С помощью класса Label вы можете создавать в окне аплета текстовые строки, например, надписи для других компонент. Эти строки не редактируются пользователем.

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

    С помощью класса Scrollbar вы можете создавать полосы просмотра, которые используются, в частности, могострочными полями редактирования текста.

    Класс TextComponent служит базовым для двух других классов - TextField и TextArea. Первый из них предназначен для создания однострочных редакторов текста, второй - для создания многострочных редкаторов текста.

    Для того чтобы понять, как компоненты размещаются на поверхности окна аплета системой Layout Manager, рассмотрим другую взаимосвязь классов Java, показанную на рис. 2.

    pic02.gif (3468 bytes)

    Рис. 2. Компоненты и контейнеры

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

    Класс Applet, так же как и другие классы, произведенные от класса Container, является контейнером. Это означает, что аплет может содержать в себе компоненты (такие как органы управления) и контейнеры.

    Заметим, что класс Applet наследуется от класса Container через класс Panel, в котором определены методы системы Layout Manager. Настраивая соответствующим образом Layout Manager, мы можем менять стратегию размещения компонент внутри окна аплета.

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

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


Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Кнопки

    Назад Вперед

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

    Класс Button

    Конструкторы

    В классе Button определены два конструктора, первый из которых позволяет создавать кнопку без надписи, а второй - кнопку с надписью. Обычно используется второй конструктор.

      public Button();
  public Button(String label);

    Методы

    • addNotify

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

    public void addNotify();

    Получение надписи на кнопке

    • getLabel
    public String getLabel();

    Получение строки параметров, отражающей состояние кнопки

    • paramString
    protected String paramString();

    Установка надписи на кнопке

    • setLabel
    public void setLabel(String  label);

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

    Обычно аплет создает в своем окне кнопки в процессе своей инициализации при обработке метода init, например:

    Button btn1;
. . .
public void init()
{
  btn1 = new Button("Button 1");
  add(btn1);
}

    Здесь мы создали кнопку с надписью Button 1. Затем мы добавили эту кнопку в контейнер, которым является окно аплета, с помощью метода add.

    Обработка событий от кнопки

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


Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Переключатели

    Назад Вперед

    Аплеты Java могут создавать в своем окне переключатели двух типов: с независимой фиксацией и с зависимой фиксацией.

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

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

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

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

    Класс Checkbox

    Переключатели с независимой и зависимой фиксацией создаются на базе класса Checkbox. Приведем прототипы конструкторов и методов этого класса:

    Конструкторы

    Создание переключателя с независимой фиксацией без названия

    public Checkbox();

    Создание переключателя с независимой фиксацией и названием

    public Checkbox(String label);

    Создание переключателя с зависимой фиксацией и названием

    public Checkbox(String label,
  CheckboxGroup group, boolean state);

    Методы

    • addNotify

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

    public void addNotify();
    • getCheckboxGroup

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

    public CheckboxGroup getCheckboxGroup();
    • getLabel

    Получение названия переключателя

    public String getLabel();
    • getState

    Определение текущего состояния переключателя

    public boolean getState();
    • paramString

    Получение строки параметров

    protected String paramString();
    • setCheckboxGroup

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

    public void setCheckboxGroup(CheckboxGroup g);
    • setLabel

    Установка названия переключателя

    public void setLabel(String label);
    • setState

    Установка нового состояния переключателя

    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 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
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Списки класса Choice

    Назад Вперед

    На базе класса Choice вы можете создать списки типа Drop Down или, как их еще называют, "выпадающие" списки. Такой список выглядит как текстовое поле высотой в одну строку, справа от которого располагается кнопка (рис. 3).

    pic03.gif (1003 bytes)

    Рис. 3. Список типа Drop Down, созданный на базе класса Choice

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

    pic04.gif (1275 bytes)

    Рис. 4. Раскрытый список, созданный на базе класса Choice

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

    Класс Choice

    Приведем описание прототипов конструктора и методов класса Choice:

    Конструктор

    public Choice();

    Методы

    • addItem

    Добавление элемента в список

    public void addItem(String item);
    • addNotify

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

    public void addNotify();
    • countItems

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

    public int countItems();
    • getItem

    Получение строки списка по номеру соответствующего ему элемента списка

    public String getItem(int index);
    • getSelectedIndex

    Получение номера текущего выбранного элемента

    public int getSelectedIndex();
    • getSelectedItem

    Получение строки, соответствующей текущему выбранному элементу списка

    public String getSelectedItem();
    • paramString

    Получение строки параметров

    protected String paramString();
    • select

    Выбор в списке элемента по заданному номеру

    public void select(int pos);
    • select

    Выбор в списке элемента по заданной строке

    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

Компоненты
Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Списки класса List

    Назад Вперед

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

    pic05.gif (1457 bytes)

    Рис. 5. Список класса List, все элементы которого помещаются в окне списка

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

    pic06.gif (1367 bytes)

    Рис. 6. Список класса List с полосой просмотра

    Описание класса List

    В классе List определено два конструктора и довольно много различных методов. Ниже мы привели краткое описание класса List:

    Конструкторы

    Конструктор без параметров

    public List();

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

    public List(int rows,
  boolean multipleSelections);

    Методы

    • addItem

    Добавление элемента в список

    public void addItem(String item);

    Добавление элемента в список с указанием номера позиции

    public void addItem(String item, int index);
    • addNotify

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

    public void addNotify();
    • allowsMultipleSelections

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

    public boolean allowsMultipleSelections();
    • clear

    Удаление из списка всех элементов

    public void clear();
    • countItems

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

    public int countItems();
    • delItem

    Удаление элемента из заданной позиции

    public void delItem(int position);
    • delItems

    Удаление нескольких элементов

    public void delItems(int start, int end);
    • deselect

    Отмена выделения элемента с заданной позицией

    public void deselect(int index);
    • getItem

    Получение строки, связанной с элементом, по позиции этого элемента

    public String getItem(int index);
    • getRows

    Определение количества элементов, которые видны в окне списка

    public int getRows();
    • getSelectedIndex

    Определение номера выделенного элемента

    public int getSelectedIndex();
    • getSelectedIndexes

    Определение номеров выделенных элементов

    public int[] getSelectedIndexes();
    • getSelectedItem

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

    public String getSelectedItem();
    • getSelectedItems

    Получение ссылки на массив строк, связанных с выделенными элементами

    public String[] getSelectedItems();
    • getVisibleIndex

    Определение номера элемента массива, который был сделан в последний раз выделенным с помощью метода makeVisible

    public int getVisibleIndex();
    • isSelected

    Проверка, является ли выделенной строка с заданным номером

    public boolean isSelected(int index);
    • makeVisible

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

    public void makeVisible(int index);

    • minimumSize

    Минимальные размеры области, необходимые для отображения списка

    public Dimension minimumSize();

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

    public Dimension minimumSize(int rows);
    • paramString

    Получение строки параметров

    protected String paramString();
    • preferredSize

    Предпочтительные размеры области, необходимые для отображения списка

    public Dimension preferredSize();

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

    public Dimension preferredSize(int rows);

    • removeNotify

    Извещение об уничтожении узла

    public void removeNotify();
    • replaceItem

    Замещение элемента списка с заданным номером

    public void replaceItem(String newValue,
  int index);
    • select

    Выделение элемента с заданным номером

    public void select(int index);
    • setMultipleSelections

    Установка или сброс режима одновременного выделения нескольких строк

    public void setMultipleSelections(boolean v);

    Создание списка класса List

    Процесс создания списка класса 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, приведенного в нашей статье.

    Обработка событий от списка класса List

    В отличие от списка класса Choice, для выбора строки (или нескольких строк) из списка класса List, пользователь должен сделать двойной щелчок левой клавишей мыши по выделенному элементу (или элементам, если выделено несколько элементов). При этом событие можно обработать переопределенным методом action, как мы это делали для списка класса Choice.

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


Текстовое поле класса Label

Компоненты
Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Текстовое поле класса Label

    Назад Вперед

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

    Класс Label

    Ниже мы привели краткое описание класса Label:

    Поля

    Поля класса Label задают способ выравнивания текстового поля

    • CENTER

    Центрирование

    public final static int CENTER;
    • LEFT

    Выравнивание по левой границе

    public final static int LEFT;
    • RIGHT

    Выравнивание по правой границе

    public final static int RIGHT;

    Конструкторы

    Создание текстового поля без текста

    public Label();

    Создание текстового поля с заданным текстом

    public Label(String label);

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

    public Label(String label, int alignment);

    Методы

    • addNotify

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

    public void addNotify();
    • getAlignment

    Определение текущего выравнивания текстового поля

    public int getAlignment();
    • getText

    Получение текста из поля

    public String getText();
    • paramString

    Получение строки параметров

    protected String paramString();
    • setAlignment

    Установка выравнивания текстового поля

    public void setAlignment(int alignment);
    • setText

    Запись текста в поле

    public void setText(String label);

    Создание поля класса Label

    Текстовое поле класса Label создается вызовом соответствующего конструктора. Например, ниже мы создали текстовое поле, указав строку, которую надо в него записать:

    Label lbTextLabel;
lbTextLabel =
  new Label("Выберите выравнивание");

    С помощью метода add вы можете добавить текстовое поле в окно аплета:

    add(lbTextLabel);

    Метод setAlignment позволяет при необходимости изменить выравнивание текста. Способ выравнивания необходимо указать через единственный параметр метода:

    lbTextLabel.setAlignment(Label.LEFT);

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


Текстовое поле класса TextField

Компоненты
Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Текстовое поле класса TextField

    Назад Вперед

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

    Класс TextField

    Приведем краткое описание класса TextField:

    Конструкторы

    Создание поля без текста

    public TextField();

    Создание поля без текста с заданной шириной

    public TextField(int cols);

    Создание поля и инициализация его текстом

    public TextField(String text);

    Создание поля заданной ширины и инициализация его текстом

    public TextField(String text,
  int cols);

    Методы

    • addNotify

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

    public void addNotify();
    • echoCharIsSet

    Проверка, установлен ли для поля эхо-символ

    public boolean echoCharIsSet();
    • getColumns

    Определение размера поля

    public int getColumns();
    • getEchoChar

    Получение текущего эхо-символа

    public char getEchoChar();
    • minimumSize

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

    public Dimension minimumSize();

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

    public Dimension minimumSize(int cols);
    • paramString

    Получение строки параметров

    protected String paramString();
    • preferredSize

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

    public Dimension preferredSize();

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

    public Dimension preferredSize(int cols);
    • setEchoCharacter

    Установка эхо-символа для отображения в поле

    public void setEchoCharacter(char c);

    Создание текстового поля класса TextField

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

    Вот фрагмент кода, в котором создается поле с текстом, имеющее ширину, достаточную для размещения 35 символов:

    TextField txt;
txt = new TextField(
  "Введите строку текста", 35);

    Созданное поле добавляется в окно аплета методом add.

    Большинство самых полезнных методов, необходимых для работы с полем класса TextField, определено в классе TextComponent, краткое описание которого мы привели ниже.

    Класс TextComponent

    Методы

    • getSelectedText

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

    public String getSelectedText();
    • getSelectionEnd

    Получение позиции конца выделенной области

    public int getSelectionEnd();
    • getSelectionStart

    Получение позиции начала выделенной области

    public int getSelectionStart();
    • getText

    Получение полного текста из поля

    public String getText();
    • isEditable

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

    public boolean isEditable();
    • paramString

    Получение строки параметров

    protected String paramString();
    • removeNotify

    Удаление извещения

    public void removeNotify();
    • select

    Выделение заданной области текста

    public void select(int selStart,
  int selEnd);
    • selectAll

    Выделение всего текста

    public void selectAll();
    • setEditable

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

    public void setEditable(boolean t);
    • setText

    Установка текста в поле

    public void setText(String t);

    С помощью метода getText вы можете получить весь текст, который имеется в поле. Метод getSelectedText позволяет получить только ту часть текста, которая предварительно была выделена пользователем.

    Приложение может выделить любой фрагмент текста или весь текст при помощи методов select и selectAll, соответственно.

    Для записи текста в поле приложение может воспользоваться методом setText.

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


Многострочное текстовое поле класса TextArea

Компоненты
Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Многострочное текстовое поле класса TextArea

    Назад Вперед

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

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

    Класс TextArea

    Краткое описание класса TextArea мы привели ниже:

    Конструкторы

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

    public TextArea();

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

    public TextArea(int rows, int cols);

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

    public TextArea(String text);

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

    public TextArea(String text,
  int rows, int cols);

    Методы

    • addNotify

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

    public void addNotify();
    • append

    Добавление текста в поле редактирования

    public void append(String str);
    • appendText

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

    public void appendText(String str);
    • getColumns

    Определение количества столбцов поля

    public int getColumns();
    • getRows

    Определение количества строк поля

    public int getRows();
    • insertText

    Добавление текста в поле редактирования начиная с заданной позиции

    public void insertText(String str, int pos);
    • minimumSize

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

    public Dimension minimumSize();

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

    public Dimension minimumSize(int rows,
   int cols);
    • paramString

    Получение строки параметров

    protected String paramString();
    • preferredSize

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

    public Dimension preferredSize();

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

    public Dimension preferredSize(int rows,
   int cols);
    • replaceText

    Замещение блока текста, начиная с первой позиции и до второй позиции

    public void replaceText(String str,
   int start, int end);

    Создание поля TextArea

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

    TextArea txt;
txt = new TextArea("Введите строку текста",
 5, 35);

    Созданное поле добавляется в окно аплета методом add.

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


Аплет FormDemo

Компоненты
Кнопки
Переключатели
Списки Choice
Списки List
Поле Label
Поле TextField
Поле TextArea
Аплет FormDemo

    Аплет FormDemo

    Назад Вперед

    В аплете FormDemo мы покажем приемы работы с компонентами, такими как переключатели, кнопки, текстовые поля и списки.

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

    pic07sm.gif (1910 bytes) Рис. 7. Окно аплета FormDemo

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

    Переключатели First и Second активизируют однострочные текстовые поля редактирования Enter your first name и Enter your second name. После того как пользователь нажмет кнопку Ready, содержимое активных полей, а также состояние переключателей Mode 1, Mode 2 и Mode 3 будет отображено в многострочном поле редактирования. Это поле находится в нижней части окна аплета.

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

    К сожалению, при изменении размеров окна аплета находящиеся в нем компоненты изменяют свое расположение. Этот недостаток мы устраним после того, как расскажем вам о системе Layout Manager, с помощью которой вы можете управлять размещением компонент в окне аплета.

    Исходный текст аплета FormDemo

    Исходный текст аплета FormDemo вы найдете в листинге 1.

    Листинг 1. Файл FormDemo.java

    import java.applet.Applet;
import java.awt.*;
import java.util.*;
    public class FormDemo extends Applet
{
  Button btReady;

  Checkbox chbox1;
  Checkbox chbox2;
      CheckboxGroup grRadio;
  Checkbox rd1;
  Checkbox rd2;
  Checkbox rd3;

  Choice ch1;
      Label lbFirstName;
  Label lbSecondName;
      TextField txtFirstName;
  TextField txtSecondName;
  TextArea  txta;
      public void init()
  {
    chbox1 = new Checkbox("First");
    add(chbox1);
        lbFirstName =
     new Label("Enter your first name:");
    add(lbFirstName);
        txtFirstName = new TextField(" ", 30);
    add(txtFirstName);
        chbox2 = new Checkbox("Second");
    add(chbox2);
        lbSecondName =
      new Label("Enter your second name:");
    add(lbSecondName);
        txtSecondName = new TextField(" ", 30);
    add(txtSecondName);
        grRadio = new CheckboxGroup();
    rd1 = new Checkbox("Mode 1",
      grRadio, true);
    rd2 = new Checkbox("Mode 2",
      grRadio, false);
    rd3 = new Checkbox("Mode 3",
      grRadio, false);
        add(rd1);
    add(rd2);
    add(rd3);
        ch1 = new Choice();
    ch1.addItem("White");
    ch1.addItem("Green");
    ch1.addItem("Yellow");
        add(ch1);
        setBackground(Color.yellow);
        lbFirstName.setBackground(Color.yellow);
    lbSecondName.setBackground(Color.yellow);
        rd1.setBackground(Color.yellow);
    rd2.setBackground(Color.yellow);
    rd3.setBackground(Color.yellow);
        chbox1.setBackground(Color.yellow);
    chbox2.setBackground(Color.yellow);
        txta = new TextArea("", 6, 45);
    add(txta);
    txta.setBackground(Color.white);
        btReady = new Button("Ready");
    add(btReady);
  }
      public String getAppletInfo()
  {
    return "Name: FormDemo";
  }
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension =
      getSize();
        g.setColor(Color.black);
    g.drawRect(0, 0,
      dimAppWndDimension.width  - 1,
      dimAppWndDimension.height - 1);
  }
      public boolean action(Event evt,
    Object obj)
  {
    Button btn;
    String str1, str2;
        if(evt.target instanceof Button)
    {
      if(evt.target.equals(btReady))
      {
        btn = (Button)evt.target;
        str1 = txtFirstName.getText();
    str2 = txtSecondName.getText();
        if(chbox1.getState())
      txta.append(str1);
        if(chbox2.getState())
      txta.append(str2);
        if(rd1.getState())
      txta.append("\nMode 1\n");
        if(rd2.getState())
      txta.append("\nMode 2\n");
        if(rd3.getState())
      txta.append("\nMode 3\n");
      }
      else
      {
    return false;
      }
      return true;
    }
    else if(evt.target instanceof Choice)
    {
      if(evt.target.equals(ch1))
      {
    if(ch1.getSelectedIndex() == 0)
      txta.setBackground(Color.white);
        if(ch1.getSelectedIndex() == 1)
      txta.setBackground(Color.green);
        if(ch1.getSelectedIndex() == 2)
      txta.setBackground(Color.yellow);
      }
    }
    return false;
  }
}

    В листинге 2 мы привели исходный текст документа HTML, созданный для нашего аплета системой Java WorkShop.

    Листинг 2. Файл FormDemo.tmp.html

    <applet
  name="FormDemo"
  code="FormDemo"
  codebase=
"file:/e:/sun/articles/vol6/src/FormDemo"
  width="500"
  height="600"
  align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
  <hr>If your browser
recognized the applet tag,
you would see an applet here.<hr>
</applet>

    Описание исходного текста

    Приведем краткое описание полей и методов, определенных в аплете FormDemo.

    Поля главного класса

    В главном классе нашего аплета мы определили несколько полей.

    Поле btReady хранит ссылку на кнопку с надписью Ready:

    Button btReady;

    В полях chbox1 и chbox2 записаны ссылки на переключатели с независимой фиксацией, которые используются для активизации однострочных текстовых полей:

    Checkbox chbox1;
Checkbox chbox2;

    Поле grRadio хранит ссылку на группу переключателей с зависимой фиксацией, определяющих режимы работы Mode 1, Mode 2 и Mode 3:

    CheckboxGroup grRadio;

    Ссылки на эти переключатели находятся в следующих трех полях:

    Checkbox rd1;
Checkbox rd2;
Checkbox rd3;

    В поле ch1 хранится ссылка на список, предназначенный для выбора цвета:

    Choice ch1;

    Слева от однострочных полей редактирования в нашем окне имеются подписи, реализованные как объекты класса Label. Ссылки на эти объекты находятся в полях lbFirstName и lbSecondName:

    Label lbFirstName;
Label lbSecondName;

    Ссылки на однострочные поля редактирования записаны в поля с именами txtFirstName и txtSecondName:

    TextField txtFirstName;
TextField txtSecondName;

    И, наконец, ссылка на многострочное текстовое поле хранится в поле с именем txta:

    TextArea  txta;

    Метод init

    Метод init занимается созданием компонент и добавлением их в окно алпета. Кроме того, этот метод изменяет цвет фона окна аплета и окон добавляемых компонент.

    Прежде всего метод init создает два переключателя с независимой фиксацией, два объекта класса Label и два однострочных поля редактирования текста:

    chbox1 = new Checkbox("First");
add(chbox1);
    lbFirstName =
   new Label("Enter your first name:");
add(lbFirstName);
    txtFirstName = new TextField(" ", 30);
add(txtFirstName);
    chbox2 = new Checkbox("Second");
add(chbox2);
    lbSecondName =
  new Label("Enter your second name:");
add(lbSecondName);
    txtSecondName = new TextField(" ", 30);
add(txtSecondName);

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

    Для группы переключателей с зависимой фиксацией мы создаем объект класса CheckboxGroup:

    grRadio = new CheckboxGroup();

    Ссылка на этот объект затем передается в качестве второго параметра конструкторам, создающим переключатели:

    rd1 = new Checkbox("Mode 1",
    grRadio, true);
rd2 = new Checkbox("Mode 2",
    grRadio, false);
rd3 = new Checkbox("Mode 3",
    grRadio, false);

    Переключатели добавляются в окно аплета при помощи метода add:

    add(rd1);
add(rd2);
add(rd3);

    Список цветов создается как объект класса Choice:

    ch1 = new Choice();

    После создания списка мы добавляем в него три элемента, вызывая для этого метод addItem:

    ch1.addItem("White");
ch1.addItem("Green");
ch1.addItem("Yellow");

    Вслед за этим мы добавляем сформированный список в окно аплета:

    add(ch1);

    Для установки цвета фона мы вызываем метод setBackground без указания объекта:

    setBackground(Color.yellow);

    В этом случае метод вызывается для текущего объекта, то есть для нашего аплета. Чтобы установить цвет фона в окнах компонент, мы вызываем метод setBackground для соответствующих объектов, как это показано ниже:

    lbFirstName.setBackground(Color.yellow);
lbSecondName.setBackground(Color.yellow);
    rd1.setBackground(Color.yellow);
rd2.setBackground(Color.yellow);
rd3.setBackground(Color.yellow);
    chbox1.setBackground(Color.yellow);
chbox2.setBackground(Color.yellow);

    Многострочное текстовое поле создается как объект класса TextArea. В нем 6 строк и 45 столбцов:

    txta = new TextArea("", 6, 45);
add(txta);

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

    txta.setBackground(Color.white);

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

    И, наконец, последнее что делает метод init перед тем как вернуть управление, - создает кнопку с надписью Ready и добавляет ее в окно аплета:

    btReady = new Button("Ready");
add(btReady);

    Метод action

    В методе action мы определили рабочие поля btn, str1 и str2:

    Button btn;
String str1, str2;

    В начале своей работы метод action определяет, какой компонент вызвал событие. Для этого анализируется поле evt.target:

    if(evt.target instanceof Button)
{
  . . .
  return true;
}
else if(evt.target instanceof Choice)
{
  . . .
  return true;
}
return false;

    Наш метод action обрабатывает события, вызываемые объектами классов Button и Choice. Если событие вызвано компонентом, относящимся к какому-либо другому классу, метод возвращает значение false. Этим он сигнализирует, что обработка события не выполнялась.

    В случае успешной обработки события метод action возвращает значение true.

    Если событие вызвано кнопкой, наш метод action проверяет, какой именно. Обработка выполняется только в том случае, если через поле evt.target передается ссылка на кнопку btReady:

    if(evt.target.equals(btReady))
{
  . . .
}
else
{
  return false;
}
  return true;

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

    Что делает обработчик события, создаваемого кнопкой?

    Прежде всего, он сохраняет ссылку на кнопку в рабочей переменной (просто для того чтобы показать, как это делается):

    btn = (Button)evt.target;

    Далее наш обработчик события извлекает текстовые строки из однострочных текстовых полей, вызывая для этого метод getText. Эти строки записываются в рабочие переменные str1 и str2:

    str1 = txtFirstName.getText();
str2 = txtSecondName.getText();

    Затемметод action проверяет состояние переключателей с независимой фиксацией chbox1 и chbox2. Если они включены, содержимое соответствующих временных переменных добавляется в многострочное текстовое поле txta:

    if(chbox1.getState())
   txta.append(str1);
    if(chbox2.getState())
   txta.append(str2);

    Для добавления мы вызываем метод append.

    Аналогичным образом преверяется состояние переключателей с зависимой фиксацией:

    if(rd1.getState())
  txta.append("\nMode 1\n");
    if(rd2.getState())
  txta.append("\nMode 2\n");
    if(rd3.getState())
  txta.append("\nMode 3\n");

    Если событие вызвано списокм цветов ch1, то метод action определяет, какая строка списка стала выделенной и устанавливает в многострочном поле редактирования соответствующий цвет фона. Для определения выделенной строки применяется метод getSelectedIndex:

    if(evt.target.equals(ch1))
{
  if(ch1.getSelectedIndex() == 0)
    txta.setBackground(Color.white);
      if(ch1.getSelectedIndex() == 1)
    txta.setBackground(Color.green);
      if(ch1.getSelectedIndex() == 2)
    txta.setBackground(Color.yellow);
}

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


Рис. 7. Окно аплета FormDemo

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

pic07.gif (6737 bytes)

Рис. 7. Окно аплета FormDemo


Работа с системой Layout Manager

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Работа с системой Layout Manager

    Назад Вперед

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

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

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

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

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


Режимы системы Layout Manager

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Режимы системы Layout Manager

    Назад Вперед

    Прежде чем мы рассмотрим различные режимы компоновки системы Layout Manager, вспомним, как происходит наследование класса Applet (рис. 1).

    pic01.gif (2563 bytes)

    Рис. 1. Наследование класса Applet

    Класс Applet наследуется от класса Panel, который, в свою очередь, наследуется от класса Container и Component. Класс Container пользуется интерфейсом LayoutManager, что позволяет выбирать для контейнеров один из нескольких режимов размещения компонент в окне контейнера.

    Что же касается класса Panel, то для него по умолчанию выбирается режим размещения компонент с названием Flow Layout. Разумеется, вы можете выбрать другой режим размещения, указав его явным образом.

    Ниже мы перечислили все возможные режимы системы Layout Manager:

    Режим размещения компонент | Описание
FlowLayout | Компоненты заполняют окно контейнера "потоком" по мере их добавления методом add. Они размещаются слева направо и сверху вниз
GridLayout | Компоненты размещаются в виде таблицы по мере добавления слева направо и сверху вниз. Для этой таблицы можно указать количество столбцов и строк
GridBagLayout | Аналогично предыдущему, однако при добавлении компонент в таблицу можно указать координаты ячейки, в которую помещается компонента
BorderLayout | При размещении компоненты указывается одно из нескольких направлений: юг, север, запад, восток, центр. Направление определяется относительно центра окна контейнера
CardLayout | Размещение компонент друг над другом в одном окне. Этот режим позволяет организовать набор диалоговых панелей в виде блокнота

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

    Далее на примере конкретного приложения мы рассмотрим использование перечисленных выше режимов системы Layout Manager.


Режим FlowLayout

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Режим FlowLayout

    Назад Вперед

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

    Класс FlowLayout

    Ниже мы привели краткое описание класса FlowLayout:

    Поля

    Следующие три поля задают способы выравнивания:

    • CENTER

    Центрирование

    public final static int CENTER;
    • LEFT

    По левой границе

    public final static int LEFT;
    • RIGHT

    По правой границе

    public final static int RIGHT;

    Конструкторы

    Без указания выравнивания и зазора между компонентами

    public FlowLayout();

    С указанием выравнивания

    public FlowLayout(int align);

    С указанием выравнивания и зазора между компонентами по вертикали и горизонтали

    public FlowLayout(int align,
  int hgap, int vgap);

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

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

    С помощью второго конструктора вы можете выбрать режим размещения с заданным выравниванием компонент в окне контейнера по горизонтали. В качестве параметров этому конструктору необходимо передавать значения FlowLayout.LEFT, FlowLayout.RIGHT, или FlowLayout.CENTER. Зазор между компонентами будет при этом равен по умолчанию 5 пикселам.

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

    Методы

    • addLayoutComponent

    Не используется

    public void addLayoutComponent(
  String name, Component comp);
    • layoutContainer

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

    public void layoutContainer(
  Container target);
    • minimumLayoutSize

    Определение минимального размера окна контейнера, необходимого для размещения всех компонент

    public Dimension minimumLayoutSize(
  Container target);
    • preferredLayoutSize

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

    public Dimension preferredLayoutSize(
  Container target);
    • removeLayoutComponent

    Удаление компоненты из контейнера

    public void removeLayoutComponent(
  Component comp);
    • toString

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

    public String toString();


Режим GridLayout

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Режим GridLayout

    Назад Вперед

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

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

    Заметим, что оба параметра rows и cols не могут быть равны нулю одновременно.

    Приведем описание конструкторов класса GridLayout.

    Конструкторы

    Создание таблицы с заданным количеством строк и столбцов

    public GridLayout(
  int rows, int cols);

    Создание таблицы с заданным количеством строк и столбцов и с заданным зазором между компонентами

    public GridLayout(int rows,
  int cols, int hgap, int vgap);

    Методы

    Методы класса GridLayout используются редко, поэтому мы их только перечислим.

    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();


Режим BorderLayout

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Режим BorderLayout

    Назад Вперед

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

    Конструкторы класса BorderLayout

    Ниже приведено краткое описание конструкторов класса BorderLayout.

    public BorderLayout();
public BorderLayout(int hgap, int vgap);

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

    Методы класса BorderLayout

    Перечислим также методы класса BorderLayout:

    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();

    Применение класса BorderLayout

    Добавляя компоненты к контейнеру, вы должны использовать метод add с двумя параметрами, первый из которых указывает направление размещения, а второй - ссылку на добавляемый объект:

    add("North",  btn1);
add("East",   btn2);
add("West",   btn3);
add("South",  btn4);
add("Center", btn5);


Режим CardLayout

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Режим CardLayout

    Назад Вперед

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

    Класс CardLayout содержит два конструктора и несколько методов.

    Конструкторы класса CardLayout

    Режим без зазоров

    public CardLayout();

    Режим с зазорами по вертикали и горизонтали между компонентами и окном контейнера

    public CardLayout(int hgap, int vgap);

    Методы класса CardLayout

    • addLayoutComponent

    Добавление компоненты с указанием имени

    public void addLayoutComponent(
  String name, Component comp);
    • first

    Отображение первой страницы блокнота

    public void first(Container target);
    • last

    Отображение последней страницы блокнота

    public void last(Container target);
    • next

    Отображение следующей страницы блокнота

    public void next(Container target);
    • previous

    Отображение предыдущей страницы блокнота

    public void previous(Container target);
    • layoutContainer

    Выполнение размещения компонент

    public void layoutContainer(
  Container target);
    • minimumLayoutSize

    Определение минимальных размеров окна, необходимых для размещения компонент

    public Dimension minimumLayoutSize(
  Container target);
    • preferredLayoutSize

    Определение предпочтительных размеров окна, необходимых для размещения компонент

    public Dimension preferredLayoutSize(
  Container target);
    • removeLayoutComponent

    Удаление заданной компоненты

    public void removeLayoutComponent(
  Component comp);
    • show

    Отображение произвольной страницы блокнота по ее имени

    public void show(
  Container target, String name);
    • toString

    Получение текстовой строки названия режима размещения

    public String toString();

    Использование режима размещения CardLayout

    Как пользоваться режимом размещения 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".


Режим GridBagLayout

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Режим GridBagLayout

    Назад Вперед

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

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

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

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


Режим GridBagLayout

Layout Manager
Режимы
FlowLayout
GridLayout
BorderLayout
CardLayout
GridBagLayout
Аплет FormLayout

    Аплет FormLayout

    Назад Вперед

    В окне аплета FormLayout (рис. 2) мы расположили те же самые органы управления, которые были использованы в предыдущем аплете FormDemo. Однако для указания способа размещения компонент мы выполнили настройку системы Layout Manager, выбрав режим GridLayout.

    pic02sm.gif (2024 bytes) Рис. 2. Окно аплета FormLayout

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

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

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

    Исходный текст аплета FormLayout

    Исходный текст аплета FormLayout практически повторяет исходный текст аплета FormDemo, рассмотренный в нашей предыдущей статье. Единственное отличие заключается в том, что в методе init мы выполнили настройку системы Layout Manager, установив режим GridLayout:

    public void init()
{
  setLayout(new GridLayout(4, 3));
  . . .
}

    Здесь для размещения компонент в окне аплета создается таблица из четырех строк и трех столбцов.

    Полный исходный текст аплета FormLayout вы найдете в листинге 1.

    Листинг 1. Файл FormLayout.java

    import java.applet.Applet;
import java.awt.*;
import java.util.*;
    public class FormLayout extends Applet
{
  Button btReady;
      Checkbox chbox1;
  Checkbox chbox2;
      CheckboxGroup grRadio;
  Checkbox rd1;
  Checkbox rd2;
  Checkbox rd3;
      Choice ch1;
      Label lbFirstName;
  Label lbSecondName;
      TextField txtFirstName;
  TextField txtSecondName;
  TextArea  txta;
      public void init()
  {
    setLayout(new GridLayout(4, 3));
        chbox1 = new Checkbox("First");
    add(chbox1);
        lbFirstName = new Label(
      "Enter your first name:");
    add(lbFirstName);
        txtFirstName = new TextField(" ", 30);
    add(txtFirstName);
        chbox2 = new Checkbox("Second");
    add(chbox2);
        lbSecondName = new Label(
     "Enter your second name:");
    add(lbSecondName);
        txtSecondName = new TextField(" ", 30);
    add(txtSecondName);
        grRadio = new CheckboxGroup();
    rd1 = new Checkbox("Mode 1",
      grRadio, true);
    rd2 = new Checkbox("Mode 2",
      grRadio, false);
    rd3 = new Checkbox("Mode 3",
      grRadio, false);
        add(rd1);
    add(rd2);
    add(rd3);
        ch1 = new Choice();
    ch1.addItem("White");
    ch1.addItem("Green");
    ch1.addItem("Yellow");
        add(ch1);
        setBackground(Color.yellow);
        lbFirstName.setBackground(
      Color.yellow);
    lbSecondName.setBackground(
      Color.yellow);
        rd1.setBackground(Color.yellow);
    rd2.setBackground(Color.yellow);
    rd3.setBackground(Color.yellow);
        chbox1.setBackground(Color.yellow);
    chbox2.setBackground(Color.yellow);
        txta = new TextArea("", 6, 45);
    add(txta);
    txta.setBackground(Color.white);
        btReady = new Button("Ready");
    add(btReady);
  }
      public String getAppletInfo()
  {
    return "Name: FormDemo";
  }
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension =
      getSize();
        g.setColor(Color.black);
    g.drawRect(0, 0,
      dimAppWndDimension.width  - 1,
      dimAppWndDimension.height - 1);
  }
      public boolean action(Event evt,
    Object obj)
  {
    Button btn;
    String str1, str2;
        if(evt.target instanceof Button)
    {
      if(evt.target.equals(btReady))
      {
        btn = (Button)evt.target;
        str1 = txtFirstName.getText();
    str2 = txtSecondName.getText();
        if(chbox1.getState())
      txta.append(str1);
        if(chbox2.getState())
      txta.append(str2);
        if(rd1.getState())
      txta.append("\nMode 1\n");
        if(rd2.getState())
      txta.append("\nMode 2\n");
        if(rd3.getState())
      txta.append("\nMode 3\n");
      }
      else
      {
    return false;
      }
      return true;
    }
    else if(evt.target instanceof Choice)
    {
      if(evt.target.equals(ch1))
      {
    if(ch1.getSelectedIndex() == 0)
      txta.setBackground(Color.white);
        if(ch1.getSelectedIndex() == 1)
      txta.setBackground(Color.green);
        if(ch1.getSelectedIndex() == 2)
      txta.setBackground(Color.yellow);
      }
    }
    return false;
  }
}

    Исходный текст документа HTML, созданный для нашего аплета системой Java WorkShop, представлен в листинге 2.

    Листинг 2. Файл FormLayout.tmp.html

    <applet
  name="FormLayout"
  code="FormLayout"
  codebase=
  "file:/e:/sun/articles/vol7/src/FormLayout"
  width="500"
  height="600"
  align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
</applet>


Рис. 2. Окно аплета FormLayout

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

pic02.gif (7045 bytes)

Рис. 2. Окно аплета FormLayout


Работа с панелями
Создание панелей
Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Работа с панелями

    Назад Вперед

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

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

    pic01.gif (3017 bytes)

    Рис. 1. Размещение нескольких панелей в окне аплета

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


Создание панелей
Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Создание панелей

    Назад Вперед

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

    setLayout(new GridLayout(2, 1));

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

    Далее нужно создать объекты класса Panel:

    Panel pTopPanel;
pTopPanel = new Panel();
Panel pBottomPanel;
pBottomPanel = new Panel();

    Ссылка на панель, которая будет располагаться сверху, записывается в переменную pTopPanel, а на ту, что будет располагаться снизу - в переменную pBottomPanel.


Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Добавление панелей

    Назад Вперед

    Создав панели, вы можете добавить их в окно аплета, вызвав метод 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.


Добавление компонент в панели

Работа с панелями
Создание панелей
Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Добавление компонент в панели

    Назад Вперед

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

    Botton btn1;
Botton btn2;
btn1 = new Button();
btn2 = new Button();
pBottomPanel.add(btn1);
pBottomPanel.add(btn2);


Рисование в окне панели

Работа с панелями
Создание панелей
Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Рисование в окне панели

    Назад Вперед

    Как вы знаете, для того чтобы что-нибудь нарисовать, необходимо вначале получить контекст отображения. Методу 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
Аплет Options
Исходный текст
Описание текста

    Создание нового класса на базе класса Panel

    Назад Вперед

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

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

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


Аплет Options
Исходный текст
Описание текста

    Аплет Options

    Назад Вперед

    Аплет Options демонстрирует методики работы с панелями, а также с различными режимами системы Layout Manager.

    В окне аплета Options мы создали три панели (рис. 2).

    pic02.gif (4687 bytes)

    Рис. 2. Окно аплета Options

    В верхней панели отображается текстовая строка First panel. Цвет и шрифт этой строки, а также цвет фона можно задавать при помощи второй панели, расположенной в центре окна нашего аплета.

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

    pic03.gif (4762 bytes)

    Рис. 3. Выбор цвета фона

    pic04.gif (4864 bytes)

    Рис. 4. Выбор цвета текста

    Нажимая кнопки Background Color, Foreground Color и Set Font, вы можете отображать нужные вам страницы блокнота. С помощью кнопок Next и Prev можно перебирать страницы блокнота в прямом или обратном направлении, соответственно.


Исходный текст аплета Options

Работа с панелями
Создание панелей
Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Исходный текст аплета Options

    Назад Вперед

    Исходный текст аплета Options представлен в листинге 1.

    Листинг 1. Файл Options.java

    import java.applet.*;
import java.awt.*;
    public class Options extends Applet
{
  FirstPanel pPanel1;
  CardPanel pCard;
  ControlPanel pControl;
      public String getAppletInfo()
  {
    return "Name: Options";
  }
      public void init()
  {
    setLayout(new GridLayout(3, 1));
        pPanel1 = new FirstPanel();
    add(pPanel1);
        pCard = new CardPanel(pPanel1);
    add(pCard);
        pControl = new ControlPanel(pCard);
    add(pControl);
        pPanel1.setBackground(Color.yellow);
    pPanel1.setForeground(Color.black);
        repaint();
  }
}
    class FirstPanel extends Panel
{
  String szFontName = "TimesRoman";
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension = getSize();
        g.drawRect(0, 0,
      dimAppWndDimension.width  - 1,
      dimAppWndDimension.height - 1);
        g.setFont(new Font(szFontName,
      Font.PLAIN, 24));
    g.drawString("First panel", 10, 50);
        super.paint(g);
  }
}
    class CardPanel extends Panel
{
  Panel pBgColor;
  Panel pFgColor;
  Panel pFont;
      Panel pControlled;
      Choice chBgColor;
  Choice chFgColor;
  Choice chFont;
      Label lbBgColor;
  Label lbFgColor;
  Label lbFont;
      public CardPanel(Panel pControlledPanel)
  {
    pControlled = pControlledPanel;
        setLayout(new CardLayout(5, 5));
        pBgColor = new Panel();
    pFgColor = new Panel();
    pFont    = new Panel();
        add("BgColor", pBgColor);
    add("FgColor", pFgColor);
    add("Font", pFont);
        chBgColor = new Choice();
    chFgColor = new Choice();
    chFont    = new Choice();
        chBgColor.add("Yellow");
    chBgColor.add("Green");
    chBgColor.add("White");
        chFgColor.add("Black");
    chFgColor.add("Red");
    chFgColor.add("Green");
        chFont.add("TimesRoman");
    chFont.add("Helvetica");
    chFont.add("Courier");
        lbBgColor = new Label("Background color");
    lbFgColor = new Label("Foreground color");
    lbFont = new Label("Font");
        pBgColor.add(lbBgColor);
    pBgColor.add(chBgColor);
        pFgColor.add(lbFgColor);
    pFgColor.add(chFgColor);
        pFont.add(lbFont);
    pFont.add(chFont);
  }
      public void paint(Graphics g)
  {
    Dimension dimAppWndDimension = getSize();
    g.drawRect(0, 0,
      dimAppWndDimension.width  - 1,
      dimAppWndDimension.height - 1);
        super.paint(g);
  }
      public boolean action(Event evt, Object obj)
  {
    Choice ch;
        if(evt.target instanceof Choice)
    {
      ch = (Choice)evt.target;
          if(evt.target.equals(chBgColor))
      {
    if(ch.getSelectedIndex() == 0)
      pControlled.setBackground(
            Color.yellow);
        else if(ch.getSelectedIndex() == 1)
      pControlled.setBackground(
            Color.green);
        else if(ch.getSelectedIndex() == 2)
      pControlled.setBackground(
            Color.white);
      }
      else if(evt.target.equals(chFgColor))
      {
    if(ch.getSelectedIndex() == 0)
      pControlled.setForeground(
            Color.black);
        else if(ch.getSelectedIndex() == 1)
      pControlled.setForeground(
            Color.red);
        else if(ch.getSelectedIndex() == 2)
      pControlled.setForeground(
            Color.green);
      }
      else if(evt.target.equals(chFont))
      {
    if(ch.getSelectedIndex() == 0)
      ((FirstPanel)pControlled).szFontName
            = "TimesRoman";
        else if(ch.getSelectedIndex() == 1)
      ((FirstPanel)pControlled).szFontName
            = "Helvetica";
        else if(ch.getSelectedIndex() == 2)
      ((FirstPanel)pControlled).szFontName
            = "Courier";
      }
      else
      {
    return false;
      }
      pControlled.repaint();
          return true;
    }
    return false;
  }
}
    class ControlPanel extends Panel
{
  Button btNext;
  Button btPrev;
  Button btBgColor;
  Button btFgColor;
  Button btFont;
  Panel pCard;
      public ControlPanel(Panel pCardPanel)
  {
    pCard = pCardPanel;
    setLayout(new GridLayout(2,3));
        btBgColor = new Button("Background Color");
    btFgColor = new Button("Foreground Color");
    btFont = new Button("Set Font");
    btNext = new Button("Next");
    btPrev = new Button("Prev");
        add(btBgColor);
    add(btFgColor);
    add(btFont);
    add(btNext);
    add(btPrev);
  }
      public boolean action(Event evt, Object obj)
  {
    if(evt.target instanceof Button)
    {
      if(evt.target.equals(btBgColor))
      {
    ((CardLayout)pCard.getLayout()).show(
            pCard, "BgColor");
      }
      else if(evt.target.equals(btFgColor))
      {
    ((CardLayout)pCard.getLayout()).show(
            pCard, "FgColor");
      }
      else if(evt.target.equals(btFont))
      {
    ((CardLayout)pCard.getLayout()).show(
            pCard, "Font");
      }
      else if(evt.target.equals(btNext))
      {
    ((CardLayout)pCard.getLayout()).next(
            pCard);
      }
      else if(evt.target.equals(btPrev))
      {
    ((CardLayout)pCard.getLayout()).
            previous(pCard);
      }
      else
      {
    return false;
      }
      return true;
    }
    return false;
  }
}

    Исходный текст документа HTML, созданный для аплета Options системой Java WorkShop, представлен в листинге 2.

    Листинг 2. Файл Options.tmp.html

    <applet name="Options"
  code="Options"
  codebase=
"file:/E:/Sun/Articles/vol8/src/Options"
  width="500"
  height="600"
  align="Top"
  alt="If you had a java-enabled browser,
 you would see an applet here.">
 <hr>If your browser recognized the applet tag,
you would see an applet here.<hr>
</applet>


Описание исходного текста аплета Options

Работа с панелями
Создание панелей
Добавление панелей
Добавление компонент
Рисование в панели
Класс на базе Panel
Аплет Options
Исходный текст
Описание текста

    Описание исходного текста аплета Options

    Назад Вперед

    Помимо основного класса Options в нашем аплете создается еще три класса для панелей с именами FirstPanel, CardPanel и ControlPanel.

    Класс FirstPanel соответствует самой верхней панели, в которой отображается строка текста First panel. Классы CardPanel и ControlPanel испльзуются для создания панелей со списками и управляющими кнопками, соответственно. Мы будем рассматривать эти классы по отдельности.

    Главный класс аплета Options

    В главном классе аплета Options мы определили три поля с именами pPanel1, pCard и pControl:

    FirstPanel   pPanel1;
CardPanel    pCard;
ControlPanel pControl;

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

    Метод init

    Прежде всего метод init устанавливает для окна аплета режим размещения GridLayout:

    setLayout(new GridLayout(3, 1));

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

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

    pPanel1 = new FirstPanel();
add(pPanel1);
    pCard = new CardPanel(pPanel1);
add(pCard);
    pControl = new ControlPanel(pCard);
add(pControl);

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

    Далее метод init устанавливает начальные значения для цвета фона и текста верхней панели:

    pPanel1.setBackground(Color.yellow);
pPanel1.setForeground(Color.black);

    Обратите внимание, что мы вызываем методы setBackground и setForeground для объекта pPanel1.

    После выполнения всех этих действий метод init перерисовывает окно аплета, вызывая метод repaint:

    repaint();

    Класс FirstPanel

    Мы создали класс FirstPanel на базе класса Panel, определив в нем одно поле типа String и переопределив метод paint:

    class FirstPanel extends Panel
{
  . . .
}

    Текстовое поле szFontName хранит название шрифта, с использованием которого в окне верхней панели отображается текстовая строка:

    String szFontName = "TimesRoman";

    Метод paint определяет текущие размеры панели и рисует вокруг нее прямоугольную рамку:

    Dimension dimAppWndDimension = getSize();
    g.drawRect(0, 0,
  dimAppWndDimension.width  - 1,
  dimAppWndDimension.height - 1);

    Далее метод paint выбирает в контекст отображения, связанный с панелью, шрифт с названием szFontName и рисует текстовую строку:

    g.setFont(new Font(szFontName,
    Font.PLAIN, 24));
g.drawString("First panel", 10, 50);

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

    Последнее действие, которое выполняет метод paint первой панели - вызов метода paint из родительского класса:

    super.paint(g);

    Это приводит к перерисовке окна аплета.

    Класс CardPanel

    С помощью класса CardPanel мы создали панель для блокнота, содержащего три страницы. Этот класс, так же как и предыдущий, создан на базе класса Panel.

    Поля класса CardPanel

    В полях pBgColor, pFgColor и pFont хранятся ссылки на панели страниц блокнота, которые мы разместим внутри панели класса CardPanel:

    Panel pBgColor;
Panel pFgColor;
Panel pFont;

    Кроме того, в поле pControlled хранится ссылка на верхнюю панель с текстовой строкой First Panel.

    Panel pControlled;

    Это поле будет проинициализировано конструктором класса CardPanel.

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

    Choice chBgColor;
Choice chFgColor;
Choice chFont;

    Три поля класса Label содержат ссылки на подписи к указанным выше спискам:

    Label lbBgColor;
Label lbFgColor;
Label lbFont;

    Конструктор класса CardPanel

    При создании объекта класса CardPanel мы передаем конструктору ссылку на верхнюю панель, параметрами которой нужно управлять. Конструктор записывает эту ссылку в поле pControlled:

    public CardPanel(Panel pControlledPanel)
{
  pControlled = pControlledPanel;
   . . .
}

    Затем конструктор устанавливает режим размещения CardLayout, оставляя зазор по вертикали и горизонтали, равный пяти пикселам:

    setLayout(new CardLayout(5, 5));

    На следующем этапе мы создаем три панели для страниц блокнота и добавляем их в панель CardPanel, задавая имена:

    pBgColor = new Panel();
pFgColor = new Panel();
pFont    = new Panel();
    add("BgColor", pBgColor);
add("FgColor", pFgColor);
add("Font", pFont);

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

    chBgColor = new Choice();
chFgColor = new Choice();
chFont    = new Choice();

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

    chBgColor.add("Yellow");
chBgColor.add("Green");
chBgColor.add("White");
    chFgColor.add("Black");
chFgColor.add("Red");
chFgColor.add("Green");
    chFont.add("TimesRoman");
chFont.add("Helvetica");
chFont.add("Courier");

    Для того чтобы снабдить списки подписями, мы создаем три объекта класса Label:

    lbBgColor = new Label("Background color");
lbFgColor = new Label("Foreground color");
lbFont = new Label("Font");

    Эти объекты, а также списки добавляются на свои страницы блокнота (то есть в свои панели):

    pBgColor.add(lbBgColor);
pBgColor.add(chBgColor);
    pFgColor.add(lbFgColor);
pFgColor.add(chFgColor);
    pFont.add(lbFont);
pFont.add(chFont);

    На этом работа метода init заканчивается.

    Метод action

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

    Вначале метод action проверяет, что событие вызвано списком класса Choice:

    if(evt.target instanceof Choice)
{
  . . .
    return true;
  }
  return false;
}

    События, связанные с изменением цвета фона, обрабатываются следующим образом:

    ch = (Choice)evt.target;
    if(evt.target.equals(chBgColor))
{
  if(ch.getSelectedIndex() == 0)
    pControlled.setBackground(
    Color.yellow);
      else if(ch.getSelectedIndex() == 1)
    pControlled.setBackground(
    Color.green);
      else if(ch.getSelectedIndex() == 2)
    pControlled.setBackground(
    Color.white);
}

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

    Аналогичным образом изменяется цвет текста и рамки для верхней панели:

    else if(evt.target.equals(chFgColor))
{
  if(ch.getSelectedIndex() == 0)
    pControlled.setForeground(
    Color.black);
    else if(ch.getSelectedIndex() == 1)
    pControlled.setForeground(
    Color.red);
    else if(ch.getSelectedIndex() == 2)
    pControlled.setForeground(
    Color.green);
}

    Для изменения шрифта мы устанавливаем новое значение переменной поля szFontName, определенной в классе FirstPanel:

    else if(evt.target.equals(chFont))
{
  if(ch.getSelectedIndex() == 0)
    ((FirstPanel)pControlled).szFontName
     = "TimesRoman";
      else if(ch.getSelectedIndex() == 1)
    ((FirstPanel)pControlled).szFontName
     = "Helvetica";
      else if(ch.getSelectedIndex() == 2)
    ((FirstPanel)pControlled).szFontName
     = "Courier";
}

    Для того чтобы адресоваться к полю szFontName, нам пришлось выполнить явное преобразование типа ссылки pControlled.

    Последнее действие, которое совершает метод action - это перерисовка окна верхней панели, которая выполняется с помощью метода repaint:

    pControlled.repaint();

    Класс ControlPanel

    Класс ControlPanel создан для нижней панели с управляющими кнопками.

    Поля класса ControlPanel

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

    Button btNext;
Button btPrev;
Button btBgColor;
Button btFgColor;
Button btFont;

    Поле pCard хранит ссылку на панель блокнота:

    Panel pCard;

    Эта ссылка инициализируется конструктором класса.

    Конструктор класса ControlPanel

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

    public ControlPanel(Panel pCardPanel)
{
  pCard = pCardPanel;
  setLayout(new GridLayout(2,3));
      btBgColor = new Button("Background Color");
  btFgColor = new Button("Foreground Color");
  btFont = new Button("Set Font");
  btNext = new Button("Next");
  btPrev = new Button("Prev");
      add(btBgColor);
  add(btFgColor);
  add(btFont);
  add(btNext);
  add(btPrev);
}

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

    Метод action

    Метод action управляет работой блокнота, отображая его страницы.

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

    if(evt.target.equals(btBgColor))
{
  ((CardLayout)pCard.getLayout()).show(
    pCard, "BgColor");
}
else if(evt.target.equals(btFgColor))
{
  ((CardLayout)pCard.getLayout()).show(
  pCard, "FgColor");
}
else if(evt.target.equals(btFont))
{
  ((CardLayout)pCard.getLayout()).show(
    pCard, "Font");
}

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

    Циклический перебор страниц блокнота выполняется с помощью методов next и previous, соответственно:

    else if(evt.target.equals(btNext))
{
  ((CardLayout)pCard.getLayout()).next(
    pCard);
}
else if(evt.target.equals(btPrev))
{
  ((CardLayout)pCard.getLayout()).
    previous(pCard);
}


Окна и диалоговые панели
Окна класса Frame
Меню MenuBar
Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Окна и диалоговые панели

    Назад Вперед

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

    В составе библиотеки классов AWT имеется несколько классов, предназначенных для работы с окнами. Это класс Window, который произошел от класса Container, и его дочерние классы - Frame, Dialog и FileDialog (рис. 1).

    pic01.gif (2857 bytes)

    Рис. 1. Иерархия классов, предназначенных для создания окон

    Окно, созданное на базе класса Frame, больше всего похоже на главное окно обычного приложения Windows. Оно может иметь главное меню, для него можно устанавливать форму курсора. Внутри такого окна можно рисовать. Так как окно класса Frame (так же как и другие окна AWT) произошли от класса Container, вы можете добавлять в них различные компоненты и панели, как мы это делали с окнами аплетов и панелей.

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

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

    Что же касается класса Window, то непосредственно этот класс редко применяется для создания окон, так как классы Frame, Dialog и FileDialog более удобны и обеспечивают все необходимые возможности.


Окна класса Frame
Меню MenuBar
Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Окна класса Frame

    Назад Вперед

    Ниже мы привели краткое описание класса Frame. Так как этот класс реализует интерфейс java.awt.MenuContainer, окно класса Frame может содержать меню.

    Поля

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

    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;

    Конструкторы

    Для класса Frame определено два конструктора:

    Создание окна без заголовка

    public Frame();

    Создание окна с заголовоком

    public Frame(String title);

    Методы

    • addNotify

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

    public void addNotify();

    dispose

    Удаление окна и освобождение связанных с ним ресурсов

    public void dispose();

    getCursorType

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

    public int getCursorType();
    • getIconImage

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

    public Image getIconImage();
    • getMenuBar

    Получение ссылки на главное меню

    public MenuBar getMenuBar();
    • getTitle

    Получение заголовка окна

    public String getTitle();
    • isResizable

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

    public boolean isResizable();
    • paramString

    Получение строки параметров

    protected String paramString();
    • remove

    Удаление компоненты меню

    public void remove(MenuComponent m);
    • setCursor

    Установка типа курсора

    public void setCursor(int cursorType);
    • setIconImage

    Установка пиктограммы

    public void setIconImage(Image image);
    • setMenuBar

    Установка главного меню

    public void setMenuBar(MenuBar mb);
    • setResizable

    Включение или выключение возомжности изменения размеров окна

    public void setResizable(boolean resizable);
    • setTitle

    Установка заголовка окна

    public void setTitle(String title);

    Применение класса Frame

    Для того чтобы создать свое окно на базе класса 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);
}


Меню в окне класса Frame

Окна и диалоговые панели
Окна класса Frame
Меню MenuBar
Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Меню в окне класса Frame

    Назад Вперед

    Как мы уже говорили, окно класса Frame может иметь главное меню (Menu Bar) или, как еще говорят, строку меню. Главное меню создается на базе класса MenuBar, краткое описание которого приведено ниже.

    Конструктор

    public MenuBar();

    Методы

    • add

    Добавление меню в главное меню окна

    public Menu add(Menu m);
    • addNotify

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

    public void addNotify();
    • countMenus

    Определение количества меню, добавленных в главное меню

    public int countMenus();
    • getHelpMenu

    Получение ссылки на меню Help

    public Menu getHelpMenu();
    • getMenu

    Получение ссылки на меню с заданным номером

    public Menu getMenu(int i);
    • remove

    Удаление меню с заданным номером из главного меню

    public void remove(int index);

    Удаление компоненты меню

    public void remove(MenuComponent m);
    • removeNotify

    Извещение об удалении меню

    public void removeNotify();
    • setHelpMenu

    Установка меню Help

    public void setHelpMenu(Menu m);

    Работа с классом MenuBar

    Для формирования главного меню окна вы должны создать объект класса 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);


Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Класс Menu

    Назад Вперед

    Для того чтобы дать вам представление о том, что можно делать с меню, приведем краткое описание класса Menu:

    Конструкторы

    Создание меню с заданным названием
    public Menu(String label);

    Создание меню с заданным названием,которое может оставаться на экране после того как пользователь отпустил клавишу мыши

    public Menu(String label, boolean tearOff);

    Методы

    • add

    Добавление элемента меню

    public MenuItem add(MenuItem mi);

    Добавление строки в меню

    public void add(String label);
    • addNotify

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

    public void addNotify();
    • addSeparator

    Добавление разделителя в меню

    public void addSeparator();
    • countItems

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

    public int countItems();
    • getItem

    Получение ссылки на элемент меню с заданным номером

    public MenuItem getItem(int index);
    • isTearOff

    Проверка, остается ли меню на экране после того как пользователь отпустил клавишу мыши

    public boolean isTearOff();
    • remove

    Удаление заданного элемента меню

    public void remove(int index);

    Удаление заданной компоненты меню

    public void remove(MenuComponent item);
    • removeNotify

    Извещение об удалении меню

    public void removeNotify();

    Работа с классом Menu

    Метод addSeparator используется для добавления в меню разделительной строки. Аналогичный результат достигается и при добавлении в меню стоки "-":

    mnHelp.add("-");

    Заметим, что вы можете просто добавлять в меню строки по их названию, пользуясь методом add(String label), либо добавлять в меню элементы класса MenuItem, вызывая метод add(MenuItem mi).


Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Класс MenuItem

    Назад Вперед

    Класс MenuItem определяет поведение отдельных элементов меню.

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

    Конструктор

    public MenuItem(String label);

    Методы

    • addNotify

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

    public void addNotify();
    • disable

    Блокирование элемента меню

    public void disable();
    • enable

    Разблокирование элемента меню

    public void enable();

    Блокирование или разблокирование элемента меню

    public void enable(boolean cond);
    • getLabel

    Получение текстовой строки меню

    public String getLabel();
    • isEnabled

    Проверка, является ли элемент меню заблокированным

    public boolean isEnabled();
    • paramString

    Получение строки параметров

    public String paramString();
    • setLabel

    Установка текстовой строки для элемента меню

    public void setLabel(String label);


Создание диалоговых панелей

Окна и диалоговые панели
Окна класса Frame
Меню MenuBar
Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Создание диалоговых панелей

    Назад Вперед

    Диалоговые панели создаются на базе класса Dialog, краткое описание которого приведено ниже.

    Конструкторы

    Создание диалоговой панели без заголовка

    public Dialog(Frame parent, boolean modal);

    Создание диалоговой панели с заголовком

    public Dialog(Frame parent,
  String title, boolean  modal);

    Методы

    • addNotify

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

    public void addNotify();
    • getTitle

    Получение строки заголовка диалоговой панели

    public String getTitle();
    • isModal

    Определение, является ли диалоговая панель модальной

    public boolean isModal();
    • isResizable

    Определение возможности изменения размеров окна диалоговой панели

    public boolean isResizable();
    • paramString

    Получение строки параметров

    protected String paramString();

    setResizable

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

    public void setResizable(boolean resizable);
    • setTitle

    Установка заголовка диалоговой панели

    public void setTitle(String title);

    Использование класса Dialog

    Для того чтобы создать свою диалоговую панель, вы должны определить новый класс, унаследовав его от класса 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, если, конечно, это соответствует логике работы вашей панели.


Приложение MenuApp
Исходный текст
Описание текста

    Приложение MenuApp

    Назад Вперед

    Автономное приложение MenuApp, работающее под управлением интерпертатора Java, демонстрирует способы создания меню. В его окне (рис. 1) имеется панель с меню File и Help.

    pic02.gif (2210 bytes)

    Рис. 1. Главное окно автономного приложения MenuApp

    В меню File мы добавили строки New и Exit, а также разделитель в виде горизонтальной линии (рис. 2).

    pic03.gif (2431 bytes)

    Рис. 2. Меню File

    Меню Help (рис. 3) содержит строки Content и About. Между ними также имеется разделительная линия.

    pic04.gif (2497 bytes)

    Рис. 3. Меню Help

    Если выбрать любую строку, кроме строки Exit из меню File, на экране появится диалоговая панель с названием выбранной строки и кнопкой OK (рис. 4).

    pic05.gif (1942 bytes)

    Рис. 4. Диалоговая панель, которая появляется при выборе строки New из меню File

    Выбор строки Exit из меню File приводит к завершению работы приложения MenuApp.


Исходный текст приложения MenuApp

Окна и диалоговые панели
Окна класса Frame
Меню MenuBar
Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Исходный текст приложения MenuApp

    Назад Вперед

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

    Листинг 1. Файл MenuApp.java

    import java.awt.*;
    public class MenuApp
{
  public static void main(String args[])
  {
    MainFrameWnd frame =
      new MainFrameWnd("MenuApp");
        frame.setSize(
    frame.getInsets().left +
      frame.getInsets().right  + 320,
      frame.getInsets().top  +
      frame.getInsets().bottom + 240);
        frame.show();
  }
}
    class MainFrameWnd extends Frame
{
  MenuBar mbMainMenuBar;
  Menu mnFile;
  Menu mnHelp;
      public MainFrameWnd(String sTitle)
  {
    super(sTitle);
        setSize(400, 200);
        setBackground(Color.yellow);
    setForeground(Color.black);
        setLayout(new FlowLayout());
        mbMainMenuBar = new MenuBar();
        mnFile = new Menu("File");
        mnFile.add("New");
    mnFile.add("-");
    mnFile.add("Exit");
        mnHelp = new Menu("Help");
        mnHelp.add("Content");
    mnHelp.add("-");
    mnHelp.add("About");
        mbMainMenuBar.add(mnFile);
    mbMainMenuBar.add(mnHelp);
        setMenuBar(mbMainMenuBar);
  }
      public void paint(Graphics g)
  {
    g.setFont(new Font(
      "Helvetica", Font.PLAIN, 12));
        g.drawString("Frame window", 10, 70);
        super.paint(g);
  }
      public boolean handleEvent(Event evt)
  {
    if(evt.id == Event.WINDOW_DESTROY)
    {
      setVisible(false);
      System.exit(0);
      return true;
    }
    else
      return super.handleEvent(evt);
  }
      public boolean action(Event evt, Object obj)
  {
    MenuItem mnItem;
        if(evt.target instanceof MenuItem)
    {
      mnItem = (MenuItem)evt.target;
          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"))
      {
        MessageBox mbox;
            mbox = new MessageBox(
          "Item Content selected",
          this, "Dialog from Frame", true);
        mbox.show();
      }
          else if(obj.equals("About"))
      {
        MessageBox mbox;
        mbox = new MessageBox(
          "Item About selected",
          this, "Dialog from Frame", true);
        mbox.show();
      }
      else
        return false;
      return true;
    }
    return false;
  }
}
    class MessageBox extends Dialog
{
  Label lbMsg;
  Button btnOK;
      public MessageBox(String sMsg,
    Frame  parent, String  sTitle,
    boolean  modal)
  {
    super(parent, sTitle, modal);
    resize(200, 100);
    setLayout(new GridLayout(2, 1));
    lbMsg = new Label(sMsg, Label.CENTER);
    add(lbMsg);
    btnOK = new Button("OK");
    add(btnOK);
  }
      public boolean handleEvent(Event evt)
  {
    if(evt.id == Event.WINDOW_DESTROY)
    {
      dispose();
      return true;
    }
    else
      return super.handleEvent(evt);
  }
      public boolean action(Event evt, Object obj)
  {
    Button btn;
    if(evt.target instanceof Button)
    {
      btn = (Button)evt.target;
          if(evt.target.equals(btnOK))
      {
        dispose();
      }
      else
        return false;
      return true;
    }
    return false;
  }
}


Описание исходного текста приложения MenuApp

Окна и диалоговые панели
Окна класса Frame
Меню MenuBar
Класс Menu
Класс MenuItem
Диалоговые панели
Приложение MenuApp
Исходный текст
Описание текста

    Описание исходного текста приложения MenuApp

    Назад Вперед

    Как мы уже говорили, приложение MenuApp работает автономно. Поэтому мы импортируем только класс java.awt.*, необходимый для работы с окнами:

    import java.awt.*;

    В нашем приложении определено три класса - MenuApp, MainFrameWnd и MessageBox.

    Класс MenuApp

    В главном классе приложения MenuApp мы определили только один метод main. Этот метод получает управление при запуске приложения.

    Первым делом метод main создает объект класса MainFrameWnd, определенного в нашем приложении:

    MainFrameWnd frame =
  new MainFrameWnd("MenuApp");

    Этот класс, созданный на базе класса Frame, определяет поведение главного окна нашего приложения.

    На втором шаге метод init настраивает размеры главного окна с учетом размеров внешней рамки и заголовка окна:

    frame.setSize(frame.getInsets().left +
  frame.getInsets().right  + 320,
  frame.getInsets().top  +
  frame.getInsets().bottom + 240);

    Поля left и right объекта класса Insets, ссылку на который возвращает метод getInsets, содержат ширину левой и правой части рамки окна, соответственно. Поле top содержит высоту верхней части рамки окна с учетом заголовка, а поле bottom - высоту нижней части рамки окна.

    Для отображения окна фрейма мы вызываем метод show, как это показано ниже:

    frame.show();

    Класс MainFrameWnd

    Класс MainFrameWnd создан на базе класса Frame:

    class MainFrameWnd extends Frame
{
  . . .
}

    В нем мы определили три поля, конструктор, методы paint, handleEvent и action.

    Поля класса MainFrameWnd

    Поле mbMainMenuBar предназанчено для хранения ссылки на главное меню приложения, создаваемое как объект класса MenuBar:

    MenuBar mbMainMenuBar;

    Поля mnFile и mnHelp хранят ссылки на меню File и Help, соответственно:

    Menu mnFile;
Menu mnHelp;

    Данные меню создаются на базе класса Menu.

    Конструктор класса MainFrameWnd

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

    public MainFrameWnd(String sTitle)
{
  super(sTitle);
  . . .
}

    Далее конструктор определяет размеры окна, вызывая для него метод setSize:

    setSize(400, 200);

    Затем мы устанавливаем для нашего окна желтый цвет фона и черный цвет изображения:

    setBackground(Color.yellow);
setForeground(Color.black);

    По умолчанию для окон класса Frame устанавливается режим добавления компонент BorderLayout. Мы изменяем этот режим на FlowLayout, вызывая метод setLayout:

    setLayout(new FlowLayout());

    Далее конструктор приступает к формированию главного меню окна. Это меню создается как объект класса MenuBar:

    mbMainMenuBar = new MenuBar();

    Затем мы создаем и наполняем меню "File":

    mnFile = new Menu("File");
    mnFile.add("New");
mnFile.add("-");
mnFile.add("Exit");

    Это меню создается на базе класса Menu. Обратите внимание, что между строками New и File расположен разделитель.

    Аналогичным образом мы добавляем в главное меню другое меню - "Help":

    mnHelp = new Menu("Help");
    mnHelp.add("Content");
mnHelp.add("-");
mnHelp.add("About");

    После своего окончательного формирования меню "File" и "Help" добавляются в главное меню окна mbMainMenuBar:

    mbMainMenuBar.add(mnFile);
mbMainMenuBar.add(mnHelp);

    И, наконец, когда главное меню будет сформировано, оно подключается к окну вызовом метода setMenuBar, как это показано ниже:

    setMenuBar(mbMainMenuBar);

    Метод paint

    Метод paint получает в качестве параметра ссылку на контекст отображения, пригодный для рисования в нашем окне. Пользуясь этим контекстом, мы устанавливаем шрифт текста и рисуем текстовую строку. Затем мы вызываем метод paint из базового класса Frame, на основе которого создан наш класс MainFrameWnd:

    public void paint(Graphics g)
{
  g.setFont(new Font(
    "Helvetica", Font.PLAIN, 12));
      g.drawString("Frame window", 10, 70);
      super.paint(g);
}

    Метод handleEvent

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

    При получении кода события Event.WINDOW_DESTROY (удаление окна) мы скрываем окно, вызывая метод setVisible с параметром false.

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

    public boolean handleEvent(Event evt)
{
  if(evt.id == Event.WINDOW_DESTROY)
  {
    setVisible(false);
    System.exit(0);
    return true;
  }
  else
    return super.handleEvent(evt);
}

    Метод action

    Этот метод обрабатывает события, возникающие при выборе строка из меню.

    В начале своей работы метод action проверяет, действительно ли событие вызвано меню:

    MenuItem mnItem;
    if(evt.target instanceof MenuItem)
{
  . . .
}
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.

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

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

    Класс MessageBox

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

    class MessageBox extends Dialog
{
  . . .
}

    В классе MessageBox есть два поля, конструктор, методы handleEvent и action.

    Поля класса MessageBox

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

    Ссылка на текстовое поле хранится в поле lbMsg, на кнопку - в поле btnOK.

    Конструктор класса 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);

    Метод handleEvent класса MessageBox

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

    if(evt.id == Event.WINDOW_DESTROY)
{
  dispose();
  return true;
}
else
  return super.handleEvent(evt);

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

    Метод action класса MessageBox

    Если пользователь нажимает кнопку OK, расположенную в окне диалоговой панели, метод action вызывает для панели метод dispose, удаляя эту панель с экрана и из памяти:

    if(evt.target.equals(btnOK))
{
  dispose();
}